com.opensymphony.xwork2.config.impl.ActionConfigMatcher Maven / Gradle / Ivy
Show all versions of xwork Show documentation
/*
* $Id: ActionConfigMatcher.java 1223 2006-11-23 20:33:15Z rainerh $
*
* Copyright 2003,2004 The Apache Software Foundation.
*
* 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.config.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.opensymphony.xwork2.util.WildcardHelper;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Matches paths against pre-compiled wildcard expressions pulled from
* action configs. It uses the wildcard matcher from the Apache Cocoon
* project. Patterns will be matched in the order they exist in the
* config file. The first match wins, so more specific patterns should be
* defined before less specific patterns.
*/
public class ActionConfigMatcher implements Serializable {
/**
*
The logging instance
*/
private static final Log log = LogFactory.getLog(ActionConfigMatcher.class);
/**
* Handles all wildcard pattern matching.
*/
private static final WildcardHelper wildcard = new WildcardHelper();
/**
* The compiled paths and their associated ActionConfig's
*/
private List compiledPaths;
/**
* Finds and precompiles the wildcard patterns from the ActionConfig
* "path" attributes. ActionConfig's will be evaluated in the order they
* exist in the config file. Only paths that actually contain a
* wildcard will be compiled. Patterns will matched strictly.
*
* @param configs An array of ActionConfig's to process
*/
public ActionConfigMatcher(Map configs) {
this(configs, false);
}
/**
* Finds and precompiles the wildcard patterns from the ActionConfig
* "path" attributes. ActionConfig's will be evaluated in the order they
* exist in the config file. Only paths that actually contain a
* wildcard will be compiled.
*
* Patterns can optionally be matched "loosely". When
* the end of the pattern matches \*[^*]\*$ (wildcard, no wildcard,
* wildcard), if the pattern fails, it is also matched as if the
* last two characters didn't exist. The goal is to support the
* legacy "*!*" syntax, where the "!*" is optional.
*
* @param configs An array of ActionConfig's to process
* @param looseMatch To loosely match wildcards or not
*/
public ActionConfigMatcher(Map configs,
boolean looseMatch) {
compiledPaths = new ArrayList();
int[] pattern;
for (String name : configs.keySet()) {
if ((name != null) && (name.indexOf('*') > -1)) {
if ((name.length() > 0) && (name.charAt(0) == '/')) {
name = name.substring(1);
}
if (log.isDebugEnabled()) {
log.debug("Compiling action config path '" + name + "'");
}
pattern = wildcard.compilePattern(name);
compiledPaths.add(new Mapping(name, pattern, configs.get(name)));
int lastStar = name.lastIndexOf('*');
if (lastStar > 1 && lastStar == name.length() - 1) {
if (name.charAt(lastStar - 1) != '*') {
pattern = wildcard.compilePattern(name.substring(0, lastStar - 1));
compiledPaths.add(new Mapping(name, pattern, configs.get(name)));
}
}
}
}
}
/**
* Matches the path against the compiled wildcard patterns.
*
* @param path The portion of the request URI for selecting a config.
* @return The action config if matched, else null
*/
public ActionConfig match(String path) {
ActionConfig config = null;
if (compiledPaths.size() > 0) {
if (log.isDebugEnabled()) {
log.debug("Attempting to match '" + path
+ "' to a wildcard pattern, "+ compiledPaths.size()
+ " available");
}
Mapping m;
HashMap vars = new HashMap();
for (Iterator i = compiledPaths.iterator(); i.hasNext();) {
m = (Mapping) i.next();
if (wildcard.match(vars, path, m.getPattern())) {
if (log.isDebugEnabled()) {
log.debug("Path matches pattern '"
+ m.getOriginal() + "'");
}
config =
convertActionConfig(path,
(ActionConfig) m.getActionConfig(), vars);
break;
}
}
}
return config;
}
/**
* Clones the ActionConfig and its children, replacing various
* properties with the values of the wildcard-matched strings.
*
* @param path The requested path
* @param orig The original ActionConfig
* @param vars A Map of wildcard-matched strings
* @return A cloned ActionConfig with appropriate properties replaced with
* wildcard-matched values
*/
protected ActionConfig convertActionConfig(String path, ActionConfig orig,
Map vars) {
String className = convertParam(orig.getClassName(), vars);
String methodName = convertParam(orig.getMethodName(), vars);
String pkgName = convertParam(orig.getPackageName(), vars);
Map params = replaceParameters(orig.getParams(), vars);
Map results = new LinkedHashMap();
for (String name : orig.getResults().keySet()) {
ResultConfig result = orig.getResults().get(name);
name = convertParam(name, vars);
String resultClassName = convertParam(result.getClassName(), vars);
Map resultParams = replaceParameters(result.getParams(), vars);
ResultConfig r = new ResultConfig(name, resultClassName, resultParams);
results.put(name, r);
}
List exs = new ArrayList();
for (ExceptionMappingConfig ex : orig.getExceptionMappings()) {
String name = convertParam(ex.getName(), vars);
String exClassName = convertParam(ex.getExceptionClassName(), vars);
String exResult = convertParam(ex.getResult(), vars);
Map exParams = replaceParameters(ex.getParams(), vars);
ExceptionMappingConfig e = new ExceptionMappingConfig(name, exClassName, exResult, exParams);
exs.add(e);
}
ActionConfig config = new ActionConfig(methodName, className, pkgName,
params, results, orig.getInterceptors(), exs);
config.setLocation(orig.getLocation());
return config;
}
/**
* Replaces parameter values
*
*
* @param orig The original parameters with placehold values
* @param vars A Map of wildcard-matched strings
*/
protected Map replaceParameters(Map orig, Map vars) {
Map map = new LinkedHashMap();
for (String key : orig.keySet()) {
map.put(key, convertParam(String.valueOf(orig.get(key)), vars));
}
return map;
}
/**
* Inserts into a value wildcard-matched strings where specified
* with the {x} syntax. If a wildcard-matched value isn't found, the
* replacement token is turned into an empty string.
*
*
* @param val The value to convert
* @param vars A Map of wildcard-matched strings
* @return The new value
*/
protected String convertParam(String val, Map vars) {
if (val == null) {
return null;
}
int len = val.length();
StringBuilder ret = new StringBuilder();
char c;
String varVal;
for (int x=0; x Stores a compiled wildcard pattern and the ActionConfig it came
* from.
*/
private class Mapping implements Serializable {
/**
* The original pattern.
*/
private String original;
/**
* The compiled pattern.
*/
private int[] pattern;
/**
* The original ActionConfig.
*/
private ActionConfig config;
/**
* Contructs a read-only Mapping instance.
*
* @param original The original pattern
* @param pattern The compiled pattern
* @param config The original ActionConfig
*/
public Mapping(String original, int[] pattern, ActionConfig config) {
this.original = original;
this.pattern = pattern;
this.config = config;
}
/**
* Gets the compiled wildcard pattern.
*
* @return The compiled pattern
*/
public int[] getPattern() {
return this.pattern;
}
/**
* Gets the ActionConfig that contains the pattern.
*
* @return The associated ActionConfig
*/
public ActionConfig getActionConfig() {
return this.config;
}
/**
* Gets the original wildcard pattern.
*
* @return The original pattern
*/
public String getOriginal() {
return this.original;
}
}
}