diff --git a/feignx-core/src/main/java/feign/template/Expression.java b/feignx-core/src/main/java/feign/template/Expression.java index ac485d5..209e79b 100644 --- a/feignx-core/src/main/java/feign/template/Expression.java +++ b/feignx-core/src/main/java/feign/template/Expression.java @@ -1,55 +1,92 @@ package feign.template; import feign.support.Assert; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Map; import java.util.Objects; +import java.util.Set; /** - * Chunk that represents an Expression that, adheres to RFC 6570, and will be resolved - * during expansion. + * Chunk that represents an Expression that, adheres to RFC 6570, and will be resolved during + * expansion. */ public abstract class Expression implements Chunk { - private final String variable; + private static final String MULTIPLE_VALUE_DELIMITER = ","; + private final Set variables = new LinkedHashSet<>(); private int limit; /** * Creates a new Expression. * - * @param variable template. + * @param variableSpecification template. */ - protected Expression(String variable) { - Assert.isNotEmpty(variable, "variable is required."); - this.variable = variable; + Expression(String variableSpecification) { + Assert.isNotEmpty(variableSpecification, "variable is required."); + + /* remove the leading and trailing braces if necessary */ + if (variableSpecification.startsWith("{")) { + variableSpecification = variableSpecification + .substring(1, variableSpecification.length() - 1); + } + + if (variableSpecification.contains(MULTIPLE_VALUE_DELIMITER)) { + /* multiple variables are present in the spec */ + String[] variableSpecifications = variableSpecification.split(MULTIPLE_VALUE_DELIMITER); + this.variables.addAll(Arrays.asList(variableSpecifications)); + } else { + this.variables.add(variableSpecification); + } this.limit = -1; } /** - * Creates a new Expression, with a prefix limiting the amount of characters to include - * during expansion. + * Creates a new Expression, with a prefix limiting the amount of characters to include during + * expansion. * - * @param variable template. - * @param limit regular variable. + * @param variables template. + * @param limit regular variables. */ - protected Expression(String variable, int limit) { - this(variable); + Expression(String variables, int limit) { + this(variables); this.limit = limit; } /** - * Expand this variable based on the value provided. + * Expand this variables based on the value provided. * - * @param value to expand. + * @param variables to expand. * @return the expanded Expression value. */ - public String expand(Object value) { - String result = this.expandInternal(value); + String expand(Map variables) { + StringBuilder expanded = new StringBuilder(); + for (String variable : this.variables) { + if (variables.containsKey(variable)) { + String result = this.expandInternal(variables.get(variable)); + if (result != null) { + + /* trim the result to the limit if present */ + if (this.limit > 0) { + result = result.substring(0, limit); + } - /* honor the limit, if present */ - return (this.limit > 0) ? result.substring(0, limit) : result; + /* append the list delimiter based on this expression type when appending additional + * values */ + if (expanded.length() != 0) { + expanded.append(","); + } + expanded.append(result); + } + } + } + return expanded.toString(); } /** - * Expand this variable based on the value provided. + * Expand this variables based on the value provided. * * @param value to expand. * @return the expanded Expression value. @@ -59,10 +96,10 @@ public String expand(Object value) { /** * Variable name for this expression. * - * @return expression variable. + * @return expression variables. */ - public String getVariable() { - return this.variable; + public Collection getVariables() { + return Collections.unmodifiableSet(this.variables); } /** @@ -77,9 +114,9 @@ public int getLimit() { @Override public String getValue() { if (this.limit > 0) { - return "{" + this.variable + ":" + this.limit + "}"; + return "{" + this.variables + ":" + this.limit + "}"; } - return "{" + this.variable + "}"; + return "{" + this.variables + "}"; } @Override @@ -91,16 +128,16 @@ public boolean equals(Object obj) { return false; } Expression that = (Expression) obj; - return variable.equals(that.variable); + return variables.equals(that.variables); } @Override public int hashCode() { - return Objects.hash(variable); + return Objects.hash(variables); } @Override public String toString() { - return "Expression [" + "variable='" + variable + "'" + ", limit=" + limit + "]"; + return "Expression [" + "variables='" + variables + "'" + ", limit=" + limit + "]"; } } diff --git a/feignx-core/src/main/java/feign/template/Expressions.java b/feignx-core/src/main/java/feign/template/Expressions.java index 7faf2b8..881b054 100644 --- a/feignx-core/src/main/java/feign/template/Expressions.java +++ b/feignx-core/src/main/java/feign/template/Expressions.java @@ -35,7 +35,7 @@ public static Expression create(String variableSpec) { + "variable: " + variableName + ". Limit provided is not a valid integer."); } } - return new SimpleExpression(variableName); + return new SimpleExpression(variableName, limit); } else { throw new IllegalArgumentException("Supplied variable specification is not valid. Please " + "see RFC 6570 for more information how to construct a variable specification"); diff --git a/feignx-core/src/main/java/feign/template/SimpleExpression.java b/feignx-core/src/main/java/feign/template/SimpleExpression.java index 5a0d8bd..abb4f3e 100644 --- a/feignx-core/src/main/java/feign/template/SimpleExpression.java +++ b/feignx-core/src/main/java/feign/template/SimpleExpression.java @@ -1,7 +1,9 @@ package feign.template; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Map; public class SimpleExpression extends Expression { @@ -9,19 +11,21 @@ protected SimpleExpression(String variable) { super(variable); } + public SimpleExpression(String variable, int limit) { + super(variable, limit); + } + @Override protected String expandInternal(Object value) { - /* collections of values are expanded differently */ if (Iterable.class.isAssignableFrom(value.getClass())) { List values = new ArrayList<>(); for (Object item : (Iterable) value) { values.add(item.toString()); } return this.expandIterable(values); + } else { + return value.toString(); } - - /* return the value, as a string */ - return value.toString(); } /** @@ -33,4 +37,10 @@ protected String expandInternal(Object value) { protected String expandIterable(List values) { return String.join(",", values); } + + private String expandValue(Object value) { + StringBuilder expanded = new StringBuilder(); + + return expanded.toString(); + } } diff --git a/feignx-core/src/main/java/feign/template/UriTemplate.java b/feignx-core/src/main/java/feign/template/UriTemplate.java index b9bd35c..cd6b5cd 100644 --- a/feignx-core/src/main/java/feign/template/UriTemplate.java +++ b/feignx-core/src/main/java/feign/template/UriTemplate.java @@ -3,6 +3,7 @@ import feign.support.StringUtils; import java.net.URI; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -65,12 +66,8 @@ public URI expand(Map variables) { * @return the expanded value or {@literal null} if the expression variable is undefined. */ private String expand(Expression expression, Map variables) { - /* look for the expression value in the variable map */ - String name = expression.getVariable(); - if (variables.containsKey(name)) { - return expression.expand(variables.get(name)); - } - return null; + /* delegate to the expression */ + return expression.expand(variables); } /** diff --git a/feignx-core/src/test/java/feign/template/SimpleExpressionTest.java b/feignx-core/src/test/java/feign/template/SimpleExpressionTest.java new file mode 100644 index 0000000..1a5c1e3 --- /dev/null +++ b/feignx-core/src/test/java/feign/template/SimpleExpressionTest.java @@ -0,0 +1,87 @@ +package feign.template; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class SimpleExpressionTest { + + @Test + void expand_withSingleVariable() { + SimpleExpression simpleExpression = new SimpleExpression("{one}"); + assertThat(simpleExpression.getVariables()).hasSize(1); + assertThat(simpleExpression.getLimit()).isEqualTo(-1); + + String result = simpleExpression.expand(Collections.singletonMap("one", "two")); + assertThat(result).isEqualTo("two"); + } + + @Test + void expand_withMultipleVariables() { + SimpleExpression simpleExpression = new SimpleExpression("{one,two,three}"); + assertThat(simpleExpression.getVariables()).hasSize(3); + assertThat(simpleExpression.getLimit()).isEqualTo(-1); + + Map variables = new LinkedHashMap<>(); + variables.put("one", "first"); + variables.put("two", "second"); + variables.put("three", "third"); + String result = simpleExpression.expand(variables); + assertThat(result).isEqualTo("first,second,third"); + } + + @Test + void expand_undefinedAreRemoved() { + SimpleExpression simpleExpression = new SimpleExpression("{one,two,three}"); + + Map variables = new LinkedHashMap<>(); + variables.put("one", "first"); + variables.put("two", "second"); + String result = simpleExpression.expand(variables); + assertThat(result).isEqualTo("first,second"); + } + + @Test + void expand_emptyAreKept() { + SimpleExpression simpleExpression = new SimpleExpression("{one,two,three}"); + + Map variables = new LinkedHashMap<>(); + variables.put("one", "first"); + variables.put("two", "second"); + variables.put("three", ""); + String result = simpleExpression.expand(variables); + assertThat(result).isEqualTo("first,second,"); + } + + @Test + void expand_withLimit() { + SimpleExpression simpleExpression = new SimpleExpression("{one}", 5); + assertThat(simpleExpression.getVariables()).hasSize(1); + assertThat(simpleExpression.getLimit()).isEqualTo(5); + + String result = simpleExpression.expand(Collections.singletonMap("one", "limited")); + assertThat(result).isEqualTo("limit"); + } + + @Test + void expand_withIterable() { + SimpleExpression simpleExpression = new SimpleExpression("{one}"); + + String result = simpleExpression.expand( + Collections.singletonMap("one", Arrays.asList("first", "second", "third"))); + assertThat(result).isEqualTo("first,second,third"); + } + + @Test + void expand_withPctEncoding() { + SimpleExpression simpleExpression = new SimpleExpression("{one}"); + + String result = simpleExpression.expand( + Collections.singletonMap("one", "Hello World")); + assertThat(result).isEqualTo("Hello%20World"); + } +} \ No newline at end of file diff --git a/mvnw.cmd b/mvnw.cmd index e5cfb0a..bfd3dc7 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -62,7 +62,7 @@ if not "%JAVA_HOME%" == "" goto OkJHome echo. echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo Please set the JAVA_HOME variables in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error @@ -73,7 +73,7 @@ if exist "%JAVA_HOME%\bin\java.exe" goto init echo. echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo Please set the JAVA_HOME variables in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error