Author: jafl
Date: Wed Feb  2 20:28:20 2011
New Revision: 1066613

URL: http://svn.apache.org/viewvc?rev=1066613&view=rev
Log:
WW-3514 fix/enhance support for include/exclude parameters

Added:
    
struts/struts2/trunk/plugins/json/src/test/java/org/apache/struts2/json/JSONCleanerTest.java
      - copied, changed from r1065873, 
struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardHelperTest.java
    
struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/util/WildcardUtil.java
    
struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardUtilTest.java
      - copied, changed from r1065873, 
struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardHelperTest.java
Modified:
    
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONCleaner.java
    
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java
    
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java
    
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONUtil.java

Modified: 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONCleaner.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONCleaner.java?rev=1066613&r1=1066612&r2=1066613&view=diff
==============================================================================
--- 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONCleaner.java
 (original)
+++ 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONCleaner.java
 Wed Feb  2 20:28:20 2011
@@ -21,14 +21,51 @@
 package org.apache.struts2.json;
 
 import java.util.Iterator;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeMap;
+import java.util.HashMap;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import com.opensymphony.xwork2.util.TextParseUtil;
+import com.opensymphony.xwork2.util.WildcardUtil;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
 
 /**
- * Isolate the process of cleaning JSON data from the Interceptor class itself.
+ * <p>Isolate the process of cleaning JSON data from the Interceptor class
+ * itself.</p>
+ * 
+ * <p>The allowed and blocked wildcard patterns, combined with
+ * defaultBlock, let you filter out values that should not be injected, in
+ * the same way that ParameterFilterInterceptor does.  Note that you can
+ * only remove values from a Map.  Removing values from a List is dangerous
+ * because it could change the meaning of the data!</p>
  */
 public abstract class JSONCleaner {
 
+    private static final Logger LOG = 
LoggerFactory.getLogger(JSONCleaner.class);
+
+    public static class Filter
+    {
+        public Pattern pattern;
+        public boolean allow;
+
+        public Filter(String pattern, boolean allow)
+        {
+            this.pattern = WildcardUtil.compileWildcardPattern(pattern);
+            this.allow = allow;
+        }
+    }
+
+    private boolean defaultBlock = false;
+    private Collection<String> allowed;
+    private Collection<String> blocked;
+    private Map<String, Filter> includesExcludesMap;
+
     public Object clean(String ognlPrefix, Object data) throws JSONException {
         if (data == null)
             return null;
@@ -54,11 +91,172 @@ public abstract class JSONCleaner {
         Iterator iter = map.entrySet().iterator();
         while (iter.hasNext()) {
             Map.Entry e = (Map.Entry) iter.next();
-            e.setValue(clean((ognlPrefix.length() > 0 ? ognlPrefix + "." : "") 
+ e.getKey(), e.getValue()));
+            String key = (ognlPrefix.length() > 0 ? ognlPrefix + "." : "") + 
e.getKey();
+            if (allow(key)) {
+                e.setValue(clean(key, e.getValue()));
+            } else {
+                LOG.debug("blocked: " + key);
+                iter.remove();
+            }
         }
         return map;
     }
 
     protected abstract Object cleanValue(String ognlName, Object data) throws 
JSONException;
 
+    private boolean allow(String ognl) {
+        Map<String, Filter> includesExcludesMap = getIncludesExcludesMap();
+
+        boolean allow = !isDefaultBlock();
+
+        if (includesExcludesMap != null) {
+            for (String currRule : includesExcludesMap.keySet()) {
+                Filter f = includesExcludesMap.get(currRule);
+                if (f.pattern.matcher(ognl).matches()) {
+                    allow = f.allow;
+                }
+            }
+        }
+
+        return allow;
+    }
+
+    /**
+     * @return the compiled list of includes and excludes
+     */
+    public Map<String, Filter> getIncludesExcludesMap() {
+        if (allowed == null && blocked == null) {
+            return includesExcludesMap;
+        }
+
+        if (includesExcludesMap == null) {
+            includesExcludesMap = new TreeMap<String, Filter>();
+
+            Map<String, Boolean> existingExpr = new HashMap<String, Boolean>();
+
+            Map<String, Map<String, String>> includePatternData = 
JSONUtil.getIncludePatternData();
+            String splitPattern = 
includePatternData.get(JSONUtil.SPLIT_PATTERN).get(JSONUtil.WILDCARD_PATTERN);
+            String joinString = 
includePatternData.get(JSONUtil.JOIN_STRING).get(JSONUtil.WILDCARD_PATTERN);
+            String arrayBegin = 
includePatternData.get(JSONUtil.ARRAY_BEGIN_STRING).get(JSONUtil.WILDCARD_PATTERN);
+            String arrayEnd = 
includePatternData.get(JSONUtil.ARRAY_END_STRING).get(JSONUtil.WILDCARD_PATTERN);
+
+            if (allowed != null) {
+                for (String a : allowed) {
+                    // Compile a pattern for each level of the object hierarchy
+                    // so cleanMap() won't short-circuit too early.
+
+                    String expr = "";
+                    for (String piece : a.split(splitPattern)) {
+                        if (expr.length() > 0) {
+                            expr += joinString;
+                        }
+                        expr += piece;
+
+                        if (!existingExpr.containsKey(expr)) {
+                            existingExpr.put(expr, Boolean.TRUE);
+
+                            String s = expr;
+                            if (piece.endsWith(arrayEnd)) {
+                                s = expr.substring(0, 
expr.lastIndexOf(arrayBegin));
+                            }
+
+                            if (s.length() > 0) {
+                                includesExcludesMap.put(s, new Filter(s, 
true));
+
+                                if (LOG.isDebugEnabled()) {
+                                    LOG.debug("Adding include wildcard 
expression: " + s);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            if (blocked != null) {
+                for (String b : blocked) {
+                    includesExcludesMap.put(b, new Filter(b, false));
+                }
+            }
+        }
+
+        return includesExcludesMap;
+    }
+
+    /**
+     * Allow external caching of the compiled result.
+     *
+     * @param map the compiled list of includes and excludes
+     */
+    public void setIncludesExcludesMap(Map<String, Filter> map) {
+        includesExcludesMap = map;
+    }
+
+    /**
+     * @return value of defaultBlock
+     */
+    public boolean isDefaultBlock() {
+        return defaultBlock;
+    }
+
+    /**
+     * @param defaultExclude The defaultExclude to set.
+     */
+    public void setDefaultBlock(boolean defaultExclude) {
+        this.defaultBlock = defaultExclude;
+    }
+
+    /**
+     * @return list of blocked wildcard patterns
+     */
+    public Collection<String> getBlockedCollection() {
+        return blocked;
+    }
+
+    /**
+     * @param blocked The blocked to set.
+     */
+    public void setBlockedCollection(Collection<String> blocked) {
+        this.blocked = blocked;
+    }
+
+    /**
+     * @param blocked The blocked paramters as comma separated String.
+     */
+    public void setBlocked(String blocked) {
+        setBlockedCollection(asCollection(blocked));
+    }
+
+    /**
+     * @return list of allowed wildcard patterns
+     */
+    public Collection<String> getAllowedCollection() {
+        return allowed;
+    }
+
+    /**
+     * @param allowed The allowed to set.
+     */
+    public void setAllowedCollection(Collection<String> allowed) {
+        this.allowed = allowed;
+    }
+
+    /**
+     * @param allowed The allowed paramters as comma separated String.
+     */
+    public void setAllowed(String allowed) {
+        setAllowedCollection(asCollection(allowed));
+    }
+
+    /**
+     * Return a collection from the comma delimited String.
+     *
+     * @param commaDelim the comma delimited String.
+     * @return A collection from the comma delimited String. Returns 
<tt>null</tt> if the string is empty.
+     */
+    private Collection<String> asCollection(String commaDelim) {
+        if (commaDelim == null || commaDelim.trim().length() == 0) {
+            return null;
+        }
+        return TextParseUtil.commaDelimitedStringToSet(commaDelim);
+    }
+
 }

Modified: 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java?rev=1066613&r1=1066612&r2=1066613&view=diff
==============================================================================
--- 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java
 (original)
+++ 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java
 Wed Feb  2 20:28:20 2011
@@ -25,8 +25,10 @@ import java.lang.reflect.InvocationTarge
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.regex.Pattern;
 
 import javax.servlet.http.HttpServletRequest;
@@ -45,6 +47,7 @@ import com.opensymphony.xwork2.ActionInv
 import com.opensymphony.xwork2.inject.Inject;
 import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
 import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.WildcardUtil;
 import com.opensymphony.xwork2.util.logging.Logger;
 import com.opensymphony.xwork2.util.logging.LoggerFactory;
 
@@ -142,14 +145,6 @@ public class JSONInterceptor extends Abs
                     rpcResponse.setError(new RPCError(message, 
RPCErrorCode.INVALID_PROCEDURE_CALL));
                     result = rpcResponse;
                 }
-
-                String json = JSONUtil.serialize(result, excludeProperties, 
includeProperties,
-                        ignoreHierarchy, excludeNullProperties);
-                json = addCallbackIfApplicable(request, json);
-                JSONUtil.writeJSONToResponse(new SerializationParams(response, 
this.defaultEncoding,
-                        this.wrapWithComments, json, true, false, noCache, -1, 
-1, prefix, contentType));
-
-                return Action.NONE;
             } else {
                 String message = "Request with content type of 
'application/json-rpc' was received but SMD is "
                         + "not enabled for this interceptor. Set 'enableSMD' 
to true to enable it";
@@ -159,8 +154,8 @@ public class JSONInterceptor extends Abs
                 result = rpcResponse;
             }
 
-            String json = JSONUtil.serialize(result, excludeProperties, 
includeProperties, ignoreHierarchy,
-                    excludeNullProperties);
+            String json = JSONUtil.serialize(result, excludeProperties, 
getIncludeProperties(),
+                    ignoreHierarchy, excludeNullProperties);
             json = addCallbackIfApplicable(request, json);
             boolean writeGzip = enableGZIP && 
JSONUtil.isGzipInRequest(request);
             JSONUtil.writeJSONToResponse(new SerializationParams(response, 
this.defaultEncoding,
@@ -400,7 +395,7 @@ public class JSONInterceptor extends Abs
      *            A comma-delimited list of regular expressions
      */
     public void setExcludeProperties(String commaDelim) {
-        List<String> excludePatterns = JSONUtil.asList(commaDelim);
+        Set<String> excludePatterns = JSONUtil.asSet(commaDelim);
         if (excludePatterns != null) {
             this.excludeProperties = new 
ArrayList<Pattern>(excludePatterns.size());
             for (String pattern : excludePatterns) {
@@ -417,12 +412,44 @@ public class JSONInterceptor extends Abs
      *            A comma-delimited list of regular expressions
      */
     public void setIncludeProperties(String commaDelim) {
-        List<String> includePatterns = JSONUtil.asList(commaDelim);
-        if (includePatterns != null) {
-            this.includeProperties = new 
ArrayList<Pattern>(includePatterns.size());
-            for (String pattern : includePatterns) {
-                this.includeProperties.add(Pattern.compile(pattern));
-            }
+        includeProperties = 
JSONUtil.processIncludePatterns(JSONUtil.asSet(commaDelim), 
JSONUtil.REGEXP_PATTERN, JSONUtil.getIncludePatternData());
+    }
+
+    /**
+     * Sets a comma-delimited list of wildcard expressions to match
+     * properties that should be included from the JSON output.  Since the
+     * patterns are only used for the JSON-RPC response, you only need to
+     * specify the elements inside your result object (and "result." is
+     * automatically prepended).
+     * 
+     * @param commaDelim
+     *            A comma-delimited list of regular expressions
+     */
+    public void setIncludeWildcards(String commaDelim) {
+        Map<String, Map<String, String>> includePatternData = 
JSONUtil.getIncludePatternData();
+        
includePatternData.get(JSONUtil.PATTERN_PREFIX).put(JSONUtil.WILDCARD_PATTERN, 
"result.");
+        includeProperties = 
JSONUtil.processIncludePatterns(JSONUtil.asSet(commaDelim), 
JSONUtil.WILDCARD_PATTERN, includePatternData);
+
+        if (includeProperties != null) {
+            includeProperties.add(Pattern.compile("id"));
+            includeProperties.add(Pattern.compile("result"));
+            includeProperties.add(Pattern.compile("error"));
+            
includeProperties.add(WildcardUtil.compileWildcardPattern("error.code"));
+        }
+    }
+
+    /**
+     * Returns the appropriate set of includes.
+     */
+    private List getIncludeProperties()
+    {
+        if (includeProperties != null && getDebug()) {
+            List<Pattern> list = new ArrayList<Pattern>(includeProperties);
+            list.add(WildcardUtil.compileWildcardPattern("error.*"));
+            return list;
+        }
+        else {
+            return includeProperties;
         }
     }
 

Modified: 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java?rev=1066613&r1=1066612&r2=1066613&view=diff
==============================================================================
--- 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java
 (original)
+++ 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java
 Wed Feb  2 20:28:20 2011
@@ -25,6 +25,7 @@ import com.opensymphony.xwork2.ActionInv
 import com.opensymphony.xwork2.Result;
 import com.opensymphony.xwork2.inject.Inject;
 import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.WildcardUtil;
 import com.opensymphony.xwork2.util.logging.Logger;
 import com.opensymphony.xwork2.util.logging.LoggerFactory;
 import org.apache.struts2.StrutsConstants;
@@ -36,8 +37,8 @@ import javax.servlet.http.HttpServletRes
 import java.io.IOException;
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 import java.util.Map;
 import java.util.regex.Pattern;
 
@@ -108,7 +109,7 @@ public class JSONResult implements Resul
      * @param commaDelim A comma-delimited list of regular expressions
      */
     public void setExcludeProperties(String commaDelim) {
-        List<String> excludePatterns = JSONUtil.asList(commaDelim);
+        Set<String> excludePatterns = JSONUtil.asSet(commaDelim);
         if (excludePatterns != null) {
             this.excludeProperties = new 
ArrayList<Pattern>(excludePatterns.size());
             for (String pattern : excludePatterns) {
@@ -118,6 +119,22 @@ public class JSONResult implements Resul
     }
 
     /**
+     * Sets a comma-delimited list of wildcard expressions to match properties
+     * that should be excluded from the JSON output.
+     * 
+     * @param commaDelim A comma-delimited list of wildcard patterns
+     */
+    public void setExcludeWildcards(String commaDelim) {
+        Set<String> excludePatterns = JSONUtil.asSet(commaDelim);
+        if (excludePatterns != null) {
+            this.excludeProperties = new 
ArrayList<Pattern>(excludePatterns.size());
+            for (String pattern : excludePatterns) {
+                
this.excludeProperties.add(WildcardUtil.compileWildcardPattern(pattern));
+            }
+        }
+    }
+
+    /**
      * @return the includeProperties
      */
     public List<Pattern> getIncludePropertiesList() {
@@ -125,63 +142,23 @@ public class JSONResult implements Resul
     }
 
     /**
-     * @param commaDelim comma delimited include string patterns
+     * Sets a comma-delimited list of regular expressions to match properties
+     * that should be included in the JSON output.
+     *
+     * @param commaDelim A comma-delimited list of regular expressions
      */
     public void setIncludeProperties(String commaDelim) {
-        List<String> includePatterns = JSONUtil.asList(commaDelim);
-        if (includePatterns != null) {
-            processIncludePatterns(includePatterns);
-        }
-    }
-
-    private void processIncludePatterns(List<String> includePatterns) {
-        includeProperties = new ArrayList<Pattern>(includePatterns.size());
-        Map<String, String> existingPatterns = new HashMap<String, String>();
-        for (String pattern : includePatterns) {
-            processPattern(existingPatterns, pattern);
-        }
-    }
-
-    private void processPattern(Map<String, String> existingPatterns, String 
pattern) {
-        // Compile a pattern for each *unique* "level" of the object
-        // hierarchy specified in the regex.
-        String[] patternPieces = pattern.split("\\\\\\.");
-
-        String patternExpr = "";
-        for (String patternPiece : patternPieces) {
-            patternExpr = processPatternPiece(existingPatterns, patternExpr, 
patternPiece);
-        }
-    }
-
-    private String processPatternPiece(Map<String, String> existingPatterns, 
String patternExpr, String patternPiece) {
-        if (patternExpr.length() > 0) {
-            patternExpr += "\\.";
-        }
-        patternExpr += patternPiece;
-
-        // Check for duplicate patterns so that there is no overlap.
-        if (!existingPatterns.containsKey(patternExpr)) {
-            existingPatterns.put(patternExpr, patternExpr);
-            if (isIndexedProperty(patternPiece)) {
-                addPattern(patternExpr.substring(0, 
patternExpr.lastIndexOf("\\[")));
-            }
-            addPattern(patternExpr);
-        }
-        return patternExpr;
+        includeProperties = 
JSONUtil.processIncludePatterns(JSONUtil.asSet(commaDelim), 
JSONUtil.REGEXP_PATTERN, JSONUtil.getIncludePatternData());
     }
 
     /**
-     * Add a pattern that does not have the indexed property matching (ie. 
list\[\d+\] becomes list).
+     * Sets a comma-delimited list of wildcard expressions to match properties
+     * that should be included in the JSON output.
+     *
+     * @param commaDelim A comma-delimited list of wildcard patterns
      */
-    private boolean isIndexedProperty(String patternPiece) {
-        return patternPiece.endsWith("\\]");
-    }
-
-    private void addPattern(String pattern) {
-        this.includeProperties.add(Pattern.compile(pattern));
-        if (LOG.isDebugEnabled()) {
-            LOG.debug("Adding include property expression:  " + pattern);
-        }
+    public void setIncludeWildcards(String commaDelim) {
+        includeProperties = 
JSONUtil.processIncludePatterns(JSONUtil.asSet(commaDelim), 
JSONUtil.WILDCARD_PATTERN, JSONUtil.getIncludePatternData());
     }
 
     public void execute(ActionInvocation invocation) throws Exception {

Modified: 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONUtil.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONUtil.java?rev=1066613&r1=1066612&r2=1066613&view=diff
==============================================================================
--- 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONUtil.java
 (original)
+++ 
struts/struts2/trunk/plugins/json/src/main/java/org/apache/struts2/json/JSONUtil.java
 Wed Feb  2 20:28:20 2011
@@ -31,7 +31,10 @@ import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.regex.Pattern;
 import java.util.zip.GZIPOutputStream;
 
@@ -41,6 +44,8 @@ import javax.servlet.http.HttpServletRes
 import org.apache.commons.lang.xwork.StringUtils;
 import org.apache.struts2.json.annotations.SMDMethod;
 
+import com.opensymphony.xwork2.util.TextParseUtil;
+import com.opensymphony.xwork2.util.WildcardUtil;
 import com.opensymphony.xwork2.util.logging.Logger;
 import com.opensymphony.xwork2.util.logging.LoggerFactory;
 
@@ -256,18 +261,10 @@ public class JSONUtil {
         }
     }
 
-    public static List<String> asList(String commaDelim) {
+    public static Set<String> asSet(String commaDelim) {
         if ((commaDelim == null) || (commaDelim.trim().length() == 0))
             return null;
-        List<String> list = new ArrayList<String>();
-        String[] split = commaDelim.split(",");
-        for (int i = 0; i < split.length; i++) {
-            String trimmed = split[i].trim();
-            if (trimmed.length() > 0) {
-                list.add(trimmed);
-            }
-        }
-        return list;
+        return TextParseUtil.commaDelimitedStringToSet(commaDelim);
     }
 
     /**
@@ -392,4 +389,103 @@ public class JSONUtil {
         String header = request.getHeader("Accept-Encoding");
         return (header != null) && (header.indexOf("gzip") >= 0);
     }
+
+    /* package */ static final String REGEXP_PATTERN = "regexp";
+    /* package */ static final String WILDCARD_PATTERN = "wildcard";
+    /* package */ static final String SPLIT_PATTERN = "split";
+    /* package */ static final String JOIN_STRING = "join";
+    /* package */ static final String ARRAY_BEGIN_STRING = "array-begin";
+    /* package */ static final String ARRAY_END_STRING = "array-end";
+    /* package */ static final String PATTERN_PREFIX = "pattern-prefix";
+
+    /* package */ static Map<String, Map<String, String>> 
getIncludePatternData()
+    {
+        Map<String, Map<String, String>> includePatternData = new 
HashMap<String, Map<String, String>>();
+
+        Map<String, String> data = new HashMap<String, String>();
+        data.put(REGEXP_PATTERN, "\\\\\\.");
+        data.put(WILDCARD_PATTERN, "\\.");
+        includePatternData.put(SPLIT_PATTERN, data);
+
+        data = new HashMap<String, String>();
+        data.put(REGEXP_PATTERN, "\\.");
+        data.put(WILDCARD_PATTERN, ".");
+        includePatternData.put(JOIN_STRING, data);
+
+        data = new HashMap<String, String>();
+        data.put(REGEXP_PATTERN, "\\[");
+        data.put(WILDCARD_PATTERN, "[");
+        includePatternData.put(ARRAY_BEGIN_STRING, data);
+
+        data = new HashMap<String, String>();
+        data.put(REGEXP_PATTERN, "\\]");
+        data.put(WILDCARD_PATTERN, "]");
+        includePatternData.put(ARRAY_END_STRING, data);
+
+        data = new HashMap<String, String>();
+        data.put(REGEXP_PATTERN, "");
+        data.put(WILDCARD_PATTERN, "");
+        includePatternData.put(PATTERN_PREFIX, data);
+
+        return includePatternData;
+    }
+
+    /* package */ static List<Pattern> processIncludePatterns(Set<String> 
includePatterns, String type, Map<String, Map<String, String>> 
includePatternData) {
+        if (includePatterns != null) {
+            List<Pattern> results = new 
ArrayList<Pattern>(includePatterns.size());
+            Map<String, String> existingPatterns = new HashMap<String, 
String>();
+            for (String pattern : includePatterns) {
+                processPattern(results, existingPatterns, pattern, type, 
includePatternData);
+            }
+            return results;
+        } else {
+            return null;
+        }
+    }
+
+    private static void processPattern(List<Pattern> results, Map<String, 
String> existingPatterns, String pattern, String type, Map<String, Map<String, 
String>> includePatternData) {
+        // Compile a pattern for each *unique* "level" of the object
+        // hierarchy specified in the regex.
+        String[] patternPieces = 
pattern.split(includePatternData.get(SPLIT_PATTERN).get(type));
+
+        String patternExpr = "";
+        for (String patternPiece : patternPieces) {
+            patternExpr = processPatternPiece(results, existingPatterns, 
patternExpr, patternPiece, type, includePatternData);
+        }
+    }
+
+    private static String processPatternPiece(List<Pattern> results, 
Map<String, String> existingPatterns, String patternExpr, String patternPiece, 
String type, Map<String, Map<String, String>> includePatternData) {
+        if (patternExpr.length() > 0) {
+            patternExpr += includePatternData.get(JOIN_STRING).get(type);
+        }
+        patternExpr += patternPiece;
+
+        // Check for duplicate patterns so that there is no overlap.
+        if (!existingPatterns.containsKey(patternExpr)) {
+            existingPatterns.put(patternExpr, patternExpr);
+            if (isIndexedProperty(patternPiece, type, includePatternData)) {
+                addPattern(results, patternExpr.substring(0, 
patternExpr.lastIndexOf(includePatternData.get(ARRAY_BEGIN_STRING).get(type))), 
type, includePatternData);
+            }
+            addPattern(results, patternExpr, type, includePatternData);
+        }
+        return patternExpr;
+    }
+
+    /**
+     * Add a pattern that does not have the indexed property matching (ie. 
list\[\d+\] becomes list).
+     */
+    private static boolean isIndexedProperty(String patternPiece, String type, 
Map<String, Map<String, String>> includePatternData) {
+        return 
patternPiece.endsWith(includePatternData.get(ARRAY_END_STRING).get(type));
+    }
+
+    private static void addPattern(List<Pattern> results, String pattern, 
String type, Map<String, Map<String, String>> includePatternData) {
+        pattern = includePatternData.get(PATTERN_PREFIX).get(type) + pattern;
+        results.add(
+            type == REGEXP_PATTERN ?
+                Pattern.compile(pattern) :
+                WildcardUtil.compileWildcardPattern(pattern));
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Adding include " + (type == REGEXP_PATTERN ? "property" 
: "wildcard") + " expression:  " + pattern);
+        }
+    }
 }

Copied: 
struts/struts2/trunk/plugins/json/src/test/java/org/apache/struts2/json/JSONCleanerTest.java
 (from r1065873, 
struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardHelperTest.java)
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/json/src/test/java/org/apache/struts2/json/JSONCleanerTest.java?p2=struts/struts2/trunk/plugins/json/src/test/java/org/apache/struts2/json/JSONCleanerTest.java&p1=struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardHelperTest.java&r1=1065873&r2=1066613&rev=1066613&view=diff
==============================================================================
--- 
struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardHelperTest.java
 (original)
+++ 
struts/struts2/trunk/plugins/json/src/test/java/org/apache/struts2/json/JSONCleanerTest.java
 Wed Feb  2 20:28:20 2011
@@ -15,42 +15,119 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.opensymphony.xwork2.util;
+package org.apache.struts2.json;
 
-import com.opensymphony.xwork2.XWorkTestCase;
+import junit.framework.TestCase;
 
+import java.util.Map;
 import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class JSONCleanerTest extends TestCase {
+
+       public void testDefaultBlock1() throws JSONException {
+
+               JSONCleaner cleaner = getCleaner();
+               cleaner.setDefaultBlock(true);
+               cleaner.setAllowed("a,c");
+
+               Map data = getData();
+               cleaner.clean("", data);
+               assertEquals(2, data.size());
+               assertEquals("x", data.get("a"));
+               assertNull(data.get("b"));
+               assertNotNull(data.get("c"));
+               assertNull(data.get("d"));
+
+       }
+
+       public void testDefaultBlock2() throws JSONException {
+
+               JSONCleaner cleaner = getCleaner();
+               cleaner.setDefaultBlock(true);
+               cleaner.setAllowed("a,c,d.x");
+
+               Map data = getData();
+               cleaner.clean("", data);
+               assertEquals(3, data.size());
+               assertEquals("x", data.get("a"));
+               assertNull(data.get("b"));
+               assertNotNull(data.get("c"));
+               assertNotNull(data.get("d"));
+               assertEquals(1, ((Map) data.get("d")).size());
+               assertEquals("a", ((Map) data.get("d")).get("x"));
+               assertNull(((Map) data.get("d")).get("y"));
+
+       }
+
+       public void testDefaultAllow1() throws JSONException {
+
+               JSONCleaner cleaner = getCleaner();
+               cleaner.setDefaultBlock(false);
+               cleaner.setBlocked("b,d");
+
+               Map data = getData();
+               cleaner.clean("", data);
+               assertEquals(2, data.size());
+               assertEquals("x", data.get("a"));
+               assertNull(data.get("b"));
+               assertNotNull(data.get("c"));
+               assertNull(data.get("d"));
+
+       }
+
+       public void testDefaultAllow2() throws JSONException {
+
+               JSONCleaner cleaner = getCleaner();
+               cleaner.setDefaultBlock(false);
+               cleaner.setBlocked("b,d.y");
+
+               Map data = getData();
+               cleaner.clean("", data);
+               assertEquals(3, data.size());
+               assertEquals("x", data.get("a"));
+               assertNull(data.get("b"));
+               assertNotNull(data.get("c"));
+               assertNotNull(data.get("d"));
+               assertEquals(1, ((Map) data.get("d")).size());
+               assertEquals("a", ((Map) data.get("d")).get("x"));
+               assertNull(((Map) data.get("d")).get("y"));
+
+       }
+
+       private JSONCleaner getCleaner() {
+
+               return new JSONCleaner() {
+                       protected Object cleanValue(String ognlName, Object 
data) throws JSONException {
+                               return data;
+                       }
+               };
+
+       }
+
+       private Map getData() {
+
+               Map data = new HashMap();
+               data.put("a", "x");
+               data.put("b", "y");
+
+               List list = new ArrayList();
+               list.add("p");
+               list.add("q");
+               list.add("r");
+               data.put("c", list);
+
+               Map map = new HashMap();
+               map.put("x", "a");
+               map.put("y", "b");
+               data.put("d", map);
+
+               return data;
 
-public class WildcardHelperTest extends XWorkTestCase {
-       
-       public void testMatch() {
-               
-               WildcardHelper wild = new WildcardHelper();
-               HashMap<String,String> matchedPatterns = new 
HashMap<String,String>();
-               int[] pattern = wild.compilePattern("wes-rules");
-               assertEquals(wild.match(matchedPatterns,"wes-rules", pattern), 
true);
-               assertEquals(wild.match(matchedPatterns, "rules-wes", pattern), 
false);
-               
-               pattern = wild.compilePattern("wes-*");
-               assertEquals(wild.match(matchedPatterns,"wes-rules", pattern), 
true);
-               assertEquals("rules".equals(matchedPatterns.get("1")), true);
-               assertEquals(wild.match(matchedPatterns, "rules-wes", pattern), 
false);
-               
-               pattern = wild.compilePattern("path/**/file");
-               assertEquals(wild.match(matchedPatterns, "path/to/file", 
pattern), true);
-               assertEquals("to".equals(matchedPatterns.get("1")), true);
-               assertEquals(wild.match(matchedPatterns, 
"path/to/another/location/of/file", pattern), true);
-               
assertEquals("to/another/location/of".equals(matchedPatterns.get("1")), true);
-               
-               pattern = wild.compilePattern("path/*/file");
-               assertEquals(wild.match(matchedPatterns, "path/to/file", 
pattern), true);
-               assertEquals("to".equals(matchedPatterns.get("1")), true);
-               assertEquals(wild.match(matchedPatterns, 
"path/to/another/location/of/file", pattern), false);
-
-               pattern = wild.compilePattern("path/*/another/**/file");
-               assertEquals(wild.match(matchedPatterns, 
"path/to/another/location/of/file", pattern), true);
-               assertEquals("to".equals(matchedPatterns.get("1")), true);
-               assertEquals("location/of".equals(matchedPatterns.get("2")), 
true);
        }
 
 }

Added: 
struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/util/WildcardUtil.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/util/WildcardUtil.java?rev=1066613&view=auto
==============================================================================
--- 
struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/util/WildcardUtil.java
 (added)
+++ 
struts/struts2/trunk/xwork-core/src/main/java/com/opensymphony/xwork2/util/WildcardUtil.java
 Wed Feb  2 20:28:20 2011
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010 Yahoo, Inc.  All rights reserved.
+ * 
+ * Licensed 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 com.opensymphony.xwork2.util;
+
+import java.util.regex.Pattern;
+
+/**
+ * Helper class to convert wildcard expression to regular expression
+ */
+public class WildcardUtil {
+
+    /**
+     * Convert wildcard pattern to Pattern
+     * @param pattern String containing wildcard pattern
+     * @return compiled regular expression as a Pattern
+     */
+    public static Pattern compileWildcardPattern(String pattern) {
+        StringBuilder buf = new StringBuilder(pattern);
+
+        for (int i=buf.length()-1; i>=0; i--)
+        {
+            char c = buf.charAt(i);
+            if (c == '*' && (i == 0 || buf.charAt(i-1) != '\\'))
+            {
+                buf.insert(i+1, '?');
+                buf.insert(i, '.');
+            }
+            else if (c == '*')
+            {
+               i--;    // skip backslash, too
+            }
+            else if (needsBackslashToBeLiteralInRegex(c))
+            {
+                buf.insert(i, '\\');
+            }
+        }
+
+        return Pattern.compile(buf.toString());
+    }
+
+    /**
+     * @param c character to test
+     * @return true if the given character must be escaped to be a literal
+     * inside a regular expression.
+     */
+
+    private static final String theSpecialRegexCharList = ".[]\\?*+{}|()^$";
+
+    public static boolean needsBackslashToBeLiteralInRegex(
+        char c)
+    {
+        return (theSpecialRegexCharList.indexOf(c) >= 0);
+    }
+
+}

Copied: 
struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardUtilTest.java
 (from r1065873, 
struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardHelperTest.java)
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardUtilTest.java?p2=struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardUtilTest.java&p1=struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardHelperTest.java&r1=1065873&r2=1066613&rev=1066613&view=diff
==============================================================================
--- 
struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardHelperTest.java
 (original)
+++ 
struts/struts2/trunk/xwork-core/src/test/java/com/opensymphony/xwork2/util/WildcardUtilTest.java
 Wed Feb  2 20:28:20 2011
@@ -19,38 +19,39 @@ package com.opensymphony.xwork2.util;
 
 import com.opensymphony.xwork2.XWorkTestCase;
 
-import java.util.HashMap;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
 
-public class WildcardHelperTest extends XWorkTestCase {
+public class WildcardUtilTest extends XWorkTestCase {
        
-       public void testMatch() {
+       public void testPattern() {
                
-               WildcardHelper wild = new WildcardHelper();
-               HashMap<String,String> matchedPatterns = new 
HashMap<String,String>();
-               int[] pattern = wild.compilePattern("wes-rules");
-               assertEquals(wild.match(matchedPatterns,"wes-rules", pattern), 
true);
-               assertEquals(wild.match(matchedPatterns, "rules-wes", pattern), 
false);
+               Pattern p = WildcardUtil.compileWildcardPattern("a*b");
+               assertTrue(p.matcher("ab").matches());
+               assertTrue(p.matcher("axyb").matches());
+               assertFalse(p.matcher("bxyb").matches());
                
-               pattern = wild.compilePattern("wes-*");
-               assertEquals(wild.match(matchedPatterns,"wes-rules", pattern), 
true);
-               assertEquals("rules".equals(matchedPatterns.get("1")), true);
-               assertEquals(wild.match(matchedPatterns, "rules-wes", pattern), 
false);
+               p = WildcardUtil.compileWildcardPattern("a\\*b");
+               assertFalse(p.matcher("ab").matches());
+               assertTrue(p.matcher("a*b").matches());
                
-               pattern = wild.compilePattern("path/**/file");
-               assertEquals(wild.match(matchedPatterns, "path/to/file", 
pattern), true);
-               assertEquals("to".equals(matchedPatterns.get("1")), true);
-               assertEquals(wild.match(matchedPatterns, 
"path/to/another/location/of/file", pattern), true);
-               
assertEquals("to/another/location/of".equals(matchedPatterns.get("1")), true);
+               p = WildcardUtil.compileWildcardPattern("a.*");
+               assertFalse(p.matcher("ab").matches());
+               assertFalse(p.matcher("ab.b").matches());
+               assertTrue(p.matcher("a.b").matches());
+               assertTrue(p.matcher("a.bc").matches());
+               assertTrue(p.matcher("a.b.c").matches());
                
-               pattern = wild.compilePattern("path/*/file");
-               assertEquals(wild.match(matchedPatterns, "path/to/file", 
pattern), true);
-               assertEquals("to".equals(matchedPatterns.get("1")), true);
-               assertEquals(wild.match(matchedPatterns, 
"path/to/another/location/of/file", pattern), false);
+               p = WildcardUtil.compileWildcardPattern("a[*]");
+               assertFalse(p.matcher("ab").matches());
+               assertFalse(p.matcher("ab[b]").matches());
+               assertTrue(p.matcher("a[b]").matches());
+               assertTrue(p.matcher("a[bc]").matches());
+               assertFalse(p.matcher("a[b].c").matches());
 
-               pattern = wild.compilePattern("path/*/another/**/file");
-               assertEquals(wild.match(matchedPatterns, 
"path/to/another/location/of/file", pattern), true);
-               assertEquals("to".equals(matchedPatterns.get("1")), true);
-               assertEquals("location/of".equals(matchedPatterns.get("2")), 
true);
+               p = WildcardUtil.compileWildcardPattern("a[*].*");
+               assertTrue(p.matcher("a[b].c").matches());
+               assertTrue(p.matcher("a[bc].cd").matches());
        }
 
 }


Reply via email to