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()); } }