package org.fluentlenium.core.url;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URIBuilder;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A simple template engine for URL parameters.
*/
public class UrlTemplate {
private static final Pattern PARAM_REGEX = Pattern.compile("\\{(.+?)\\}");
private final String template;
private final List<String> parameterNames = new ArrayList<>();
private final Map<String, UrlParameter> parameters = new LinkedHashMap<>();
private final Map<UrlParameter, String> properties = new LinkedHashMap<>();
/**
* Creates a new template
*
* @param template template string
*/
public UrlTemplate(String template) {
this.template = template;
Matcher matcher = PARAM_REGEX.matcher(template);
while (matcher.find()) {
String match = matcher.group(0);
String group = matcher.group(1);
boolean optional = group.startsWith("?");
int lastIndex = group.lastIndexOf('/');
String parameterName;
String path;
if (lastIndex > -1 && lastIndex < group.length()) {
path = group.substring(optional ? 1 : 0, lastIndex + 1);
parameterName = group.substring(lastIndex + 1);
} else if (group.startsWith("?")) {
path = null;
parameterName = group.substring(1);
} else {
path = null;
parameterName = group;
}
UrlParameter parameter = new UrlParameter(parameterName, group, path, match, optional);
if (parameters.containsKey(parameter.getName())) {
throw new IllegalStateException(
String.format("Multiple parameters are defined with the same name (%s).", parameter.getName()));
}
parameters.put(parameter.getName(), parameter);
parameterNames.add(parameter.getName());
}
}
/**
* Remove all property values.
*/
public void clear() {
properties.clear();
}
/**
* Get all declared parameter parameters of this template.
*
* @return declared parameter parameters
*/
public List<UrlParameter> getParameters() {
return new ArrayList<>(parameters.values());
}
/**
* Add property value.
*
* @param value property value
* @return {@code this} reference to chain calls
*/
public UrlTemplate add(String value) {
properties.put(parameters.get(parameterNames.get(properties.size())), value);
return this;
}
/**
* Set property value.
*
* @param name name
* @param value value
* @return {@code this} reference to chain calls
*/
public UrlTemplate put(String name, String value) {
UrlParameter parameter = parameters.get(name);
if (parameter == null) {
throw new IllegalArgumentException("Invalid parameter name: " + name);
}
properties.put(parameter, value);
return this;
}
/**
* Add all property properties
*
* @param values property properties
* @return {@code this} reference to chain calls
*/
public UrlTemplate addAll(List<String> values) {
for (String value : values) {
add(value);
}
return this;
}
/**
* Set property properties.
*
* @param values properties
* @return {@code this} reference to chain calls
*/
public UrlTemplate put(Map<String, String> values) {
values.putAll(values);
return this;
}
/**
* Render the string.
*
* @return rendered url, based on template with parameters applied.
*/
public String render() {
String rendered = template;
for (UrlParameter parameter : parameters.values()) {
Object value = properties.get(parameter);
String group = parameter.getGroup();
if (value == null && !parameter.isOptional()) {
throw new IllegalArgumentException(String.format("Value for parameter %s is missing.", parameter));
} else {
rendered = rendered.replaceAll(Pattern.quote(String.format("{%s}", group)),
buildRenderReplacement(parameter, value == null ? null : String.valueOf(value)));
}
}
return rendered;
}
private String buildRenderReplacement(UrlParameter parameter, String value) {
if (value == null || value.isEmpty()) {
return "";
}
String path = parameter.getPath();
if (path != null) {
return path + value;
}
return value;
}
private String buildParsePattern() {
String fixedTemplate = template;
if (fixedTemplate.startsWith("/")) {
fixedTemplate = fixedTemplate.replaceFirst("/", "/?");
} else {
fixedTemplate = "/?" + fixedTemplate;
}
if (fixedTemplate.endsWith("/")) {
fixedTemplate = fixedTemplate + "?";
} else {
fixedTemplate = fixedTemplate + "/?";
}
for (UrlParameter parameter : parameters.values()) {
String replacementPattern = "%s([^/]+)";
if (parameter.isOptional()) {
replacementPattern = "(?:" + replacementPattern + ")?";
}
fixedTemplate = fixedTemplate.replaceAll(Pattern.quote(parameter.getMatch()),
String.format(replacementPattern, parameter.getPath() == null ? "" : parameter.getPath()));
}
return fixedTemplate;
}
/**
* Get properties from string
*
* @param input string
* @return properties
*/
public ParsedUrlTemplate parse(String input) {
String noQueryInput = input;
List<NameValuePair> queryParameters = new ArrayList<>();
try {
URIBuilder uriBuilder = new URIBuilder(input);
queryParameters = uriBuilder.getQueryParams();
uriBuilder.setCustomQuery(null);
noQueryInput = uriBuilder.toString();
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
Pattern pathRegex = Pattern.compile(buildParsePattern());
Matcher matcher = pathRegex.matcher(noQueryInput);
boolean matches = matcher.matches();
LinkedHashMap<String, String> parsedValues = new LinkedHashMap<>();
if (matches) {
for (int i = 0; i < parameterNames.size() && i < matcher.groupCount(); i++) {
String value = matcher.group(i + 1);
if (value != null) {
parsedValues.put(parameterNames.get(i), value);
}
}
}
return new ParsedUrlTemplate(matches, parsedValues, queryParameters);
}
}