package restservices.util; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; public class UriTemplate { private static final String PARAMNAME_REGEX = "\\{([a-zA-Z_0-9]+)\\}"; // http://tools.ietf.org/html/rfc3986#section-2.2 private static final String QUERYPARAM_REGEX = "([^:/?#\\[\\]@!$&'()*+,;=]+?)"; Pattern regex; List<String> paramNames = Lists.newArrayList(); private String pathString; public UriTemplate(String pathString) { Preconditions.checkNotNull(pathString); this.pathString = pathString; String re = Pattern.quote(Utils.removeLeadingAndTrailingSlash(pathString)); re = re.replaceAll("(^\\\\Q|\\\\E$)", ""); re = "^\\/*" + re + "\\/*$"; re = regexReplaceAll(re, PARAMNAME_REGEX, new Function<MatchResult, String>() { @Override public String apply(MatchResult match) { paramNames.add(match.group(1)); return QUERYPARAM_REGEX; } }); regex = Pattern.compile(re, Pattern.CASE_INSENSITIVE); } public boolean match(String uri, Map<String, String> params) { Preconditions.checkNotNull(uri); Matcher matcher = regex.matcher(uri); if (!matcher.matches()) return false; for (int i = 0; i < matcher.groupCount(); i++) { params.put(paramNames.get(i), Utils.urlDecode(matcher.group(i + 1))); } return true; } //From community commons StringUtils 4.3.3 public static String regexReplaceAll(String source, String regexString, Function<MatchResult, String> replaceFunction) { if (source == null || source.trim().isEmpty()) // avoid NPE's, save CPU return ""; StringBuffer resultString = new StringBuffer(); Pattern regex = Pattern.compile(regexString); Matcher regexMatcher = regex.matcher(source); while (regexMatcher.find()) { MatchResult match = regexMatcher.toMatchResult(); String value = replaceFunction.apply(match); regexMatcher.appendReplacement(resultString, Matcher.quoteReplacement(value)); } regexMatcher.appendTail(resultString); return resultString.toString(); } public List<String> getTemplateVariables() { return Collections.unmodifiableList(paramNames); } @Override public String toString() { return String.format("%s[path=%s]", this.getClass().getSimpleName(), pathString); } public String createURI(final Map<String, String> values) { Preconditions.checkNotNull(values); Preconditions.checkArgument(values.keySet().equals(new HashSet<String>(paramNames)), "Incomplete set of values for path " + pathString + ", expected the following keys: " + paramNames + ", found: " + values.keySet()); return regexReplaceAll(pathString, PARAMNAME_REGEX, new Function<MatchResult, String>() { @Override public String apply(MatchResult match) { String paramName = match.group(1); String value = values.get(paramName); if (value == null || value.isEmpty()) { throw new IllegalArgumentException("No value was defined for path element '{" + paramName + "}'. The value should be non-empty."); } return Utils.urlEncode(values.get(paramName)); } }); } }