diff --git a/pom.xml b/pom.xml
index a941307..283e6eb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,6 +30,7 @@
5.0.0-SNAPSHOT
+ 0.0.50
${project.basedir}/src/main/resources/META-INF/MANIFEST.MF
@@ -61,6 +62,11 @@
vertx-docgen
provided
+
+ io.github.std-uritemplate
+ std-uritemplate
+ ${std-uritemplate.version}
+
diff --git a/src/main/java/io/vertx/uritemplate/UriTemplate.java b/src/main/java/io/vertx/uritemplate/UriTemplate.java
index 31de579..d08defe 100644
--- a/src/main/java/io/vertx/uritemplate/UriTemplate.java
+++ b/src/main/java/io/vertx/uritemplate/UriTemplate.java
@@ -35,7 +35,7 @@ public interface UriTemplate {
* @throws IllegalArgumentException when the template
*/
static UriTemplate of(String uri) {
- return new UriTemplateImpl.Parser().parseURITemplate(uri);
+ return new UriTemplateImpl(uri);
}
/**
diff --git a/src/main/java/io/vertx/uritemplate/impl/UriTemplateImpl.java b/src/main/java/io/vertx/uritemplate/impl/UriTemplateImpl.java
index dd62994..ae5ad77 100644
--- a/src/main/java/io/vertx/uritemplate/impl/UriTemplateImpl.java
+++ b/src/main/java/io/vertx/uritemplate/impl/UriTemplateImpl.java
@@ -11,736 +11,108 @@
package io.vertx.uritemplate.impl;
-import io.netty.util.collection.CharObjectHashMap;
-import io.netty.util.collection.CharObjectMap;
+import io.github.stduritemplate.StdUriTemplate;
import io.vertx.uritemplate.ExpandOptions;
import io.vertx.uritemplate.UriTemplate;
import io.vertx.uritemplate.Variables;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CoderResult;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static io.vertx.uritemplate.impl.UriTemplateImpl.Parser.isHEXDIG;
public class UriTemplateImpl implements UriTemplate {
+ private String uri;
- private static final String HEX_ALPHABET = "0123456789ABCDEF";
-
- interface CharSet {
- CharSet UNRESERVED = Parser::isUnreserved;
- CharSet LITERALS = ch -> Parser.isUnreserved(ch) || Parser.isReserved(ch);
- boolean contains(char ch);
- }
-
- public abstract static class Term {
-
- }
-
- public abstract static class SOperator {
-
- private final CharSet allowedSet;
- private final String prefix;
- private final String delimiter;
- final char[] chars;
-
- SOperator(CharSet allowedSet, String prefix, String delimiter, char... chars) {
- this.allowedSet = allowedSet;
- this.prefix = prefix;
- this.delimiter = delimiter;
- this.chars = chars;
- }
-
- String join(boolean entry, String name, String value) {
- throw new UnsupportedOperationException();
- }
-
- String encodeName(String s) {
- return encode(s, false);
- }
-
- String encodeValue(String s) {
- return encode(s, false);
- }
-
- String encode(String s, boolean allowPctEncoded) {
- StringBuilder sb = new StringBuilder();
- encodeString(s, allowedSet, allowPctEncoded, sb);
- return sb.toString();
- }
- }
-
- /**
- * Operator implementation issuing a {@code key=value} only for exploded maps, e.g
- */
- private static class Cat1 extends SOperator {
-
- public Cat1(CharSet allowedSet, String prefix, String delimiter, char... chars) {
- super(allowedSet, prefix, delimiter, chars);
- }
-
- @Override
- String join(boolean entry, String name, String value) {
- return entry ? name + "=" + value : value;
- }
- }
-
- /**
- * Operator implementation always issuing as {@code key=value}, e.g form style query expansion
- */
- private static class Cat2 extends SOperator {
-
- public Cat2(CharSet allowedSet, String prefix, String delimiter, char... chars) {
- super(allowedSet, prefix, delimiter, chars);
- }
-
- @Override
- String join(boolean entry, String name, String value) {
- return name + "=" + value;
- }
- }
-
- private static class SimpleStringExpansion extends Cat1 {
- public SimpleStringExpansion() {
- super(CharSet.UNRESERVED, "", ",");
- }
- }
-
- private static class ReservedExpansion extends Cat1 {
- public ReservedExpansion() {
- super(cp -> Parser.isReserved(cp) || Parser.isUnreserved(cp), "", ",", '+');
- }
-
- @Override
- String encodeValue(String s) {
- return super.encode(s, true);
- }
- }
-
- private static class FragmentExpansion extends Cat1 {
- public FragmentExpansion() {
- super(cp -> Parser.isReserved(cp) || Parser.isUnreserved(cp), "#", ",", '#');
- }
-
- String encodeValue(String s) {
- return super.encode(s, true);
- }
- }
-
- private static class LabelExpansionWithDotPrefix extends Cat1 {
- public LabelExpansionWithDotPrefix() {
- super(CharSet.UNRESERVED, ".", ".", '.');
- }
- }
-
- private static class PathSegmentExpansion extends Cat1 {
- public PathSegmentExpansion() {
- super(CharSet.UNRESERVED, "/", "/", '/');
- }
- }
-
- private static class PathStyleParameterExpansion extends Cat2 {
- public PathStyleParameterExpansion() {
- super(CharSet.UNRESERVED, ";", ";", ';');
- }
-
- @Override
- String join(boolean entry, String name, String value) {
- if (!entry && value.isEmpty()) {
- return name;
- }
- return super.join(entry, name, value);
- }
- }
-
- private static class FormStyleQueryExpansion extends Cat2 {
- public FormStyleQueryExpansion() {
- super(CharSet.UNRESERVED, "?", "&", '?');
- }
- }
-
- private static class FormStyleQueryContinuation extends Cat2 {
- public FormStyleQueryContinuation() {
- super(CharSet.UNRESERVED, "&", "&", '&');
- }
+ public UriTemplateImpl(String uri) {
+ this.uri = uri;
}
- private static class Future extends SOperator {
- public Future() {
- super(CharSet.UNRESERVED, "", "", '=', ',', '!', '@', '|');
+ @Override
+ public String expandToString(Variables variables) {
+ Map substs = new HashMap<>();
+ for (String key: variables.names()) {
+ substs.put(key, variables.get(key));
}
+ return StdUriTemplate.expand(uri, substs);
}
- public enum Operator {
-
- SIMPLE_STRING_EXPANSION(new SimpleStringExpansion()),
- RESERVED_EXPANSION(new ReservedExpansion()),
- LABEL_EXPANSION_WITH_DOT_PREFIX(new LabelExpansionWithDotPrefix()),
- PATH_SEGMENT_EXPANSION(new PathSegmentExpansion()),
- PATH_STYLE_PARAMETER_EXPANSION(new PathStyleParameterExpansion()),
- FORM_STYLE_QUERY_EXPANSION(new FormStyleQueryExpansion()),
- FORM_STYLE_QUERY_CONTINUATION(new FormStyleQueryContinuation()) ,
- FRAGMENT_EXPANSION(new FragmentExpansion()),
- FUTURE(new Future())
-
- ;
-
- private final SOperator so;
-
- Operator(SOperator s) {
- this.so = s;
- }
-
- void expand(List variableList, Variables variables, boolean allowVariableMiss, StringBuilder sb) {
- List l = new ArrayList<>();
- for (Varspec variable : variableList) {
- Object o = variables.get(variable.varname);
- List values;
- if (o == null) {
- if (!allowVariableMiss) {
- throw new NoSuchElementException("Variable " + variable.varname + " is missing");
- }
- continue;
- } else if (o instanceof String) {
- String s = (String) o;
- if (variable.maxLength > 0 && variable.maxLength < s.length()) {
- s = s.substring(0, variable.maxLength);
- }
- values = Collections.singletonList(format(variable, s));
- } else if (o instanceof List) {
- if (variable.maxLength > 0) {
- throw new IllegalArgumentException();
- }
- List list = (List) o;
- if (list.isEmpty()) {
- continue;
- }
- values = format(variable, list);
- } else if (o instanceof Map) {
- if (variable.maxLength > 0) {
- throw new IllegalArgumentException();
- }
- Map map = (Map) o;
- if (map.isEmpty()) {
- continue;
- }
- values = format(variable, map);
- } else {
- throw new UnsupportedOperationException();
+ @Override
+ public String expandToString(Variables variables, ExpandOptions options) {
+ if (!options.getAllowVariableMiss()) {
+ for (String token: extractTokens()) {
+ if (!variables.names().contains(token)) {
+ throw new NoSuchElementException("Variable " + token + " is missing");
}
- l.addAll(values);
- }
- if (l.size() > 0) {
- sb.append(format(l));
- }
- }
-
- String format(Varspec variable, String value) {
- return so.join(false, so.encodeName(variable.decoded), so.encodeValue(value));
- }
-
- List format(Varspec variable, List value) {
- if (variable.exploded) {
- return value.stream().map(v -> format(variable, v)).collect(Collectors.toList());
- } else {
- return Collections.singletonList(so.join(false, so.encodeName(variable.decoded), value.stream().map(so::encodeValue).collect(Collectors.joining(","))));
- }
- }
-
- List format(Varspec variable, Map value) {
- if (variable.exploded) {
- return value.entrySet().stream().map(entry -> so.join(true, so.encodeName(entry.getKey()), so.encodeValue(entry.getValue()))).collect(Collectors.toList());
- } else {
- return Collections.singletonList(so.join(false, so.encodeName(variable.varname), value.entrySet().stream().flatMap(entry -> Stream.of(so.encodeValue(entry.getKey()), so.encodeValue(entry.getValue()))).collect(Collectors.joining(","))));
- }
- }
-
- String format(List l) {
- return l.stream().collect(Collectors.joining(so.delimiter, so.prefix, ""));
- }
- }
-
- private static final CharObjectMap mapping;
-
- static {
- CharObjectMap m = new CharObjectHashMap<>();
- for (Operator op : Operator.values()) {
- for (char ch : op.so.chars) {
- m.put(ch, op);
}
}
- mapping = m;
- }
-
- public static final class Literals extends Term {
- private final String value;
- private Literals(String value) {
- this.value = value;
- }
+ return expandToString(variables);
}
- public static final class Expression extends Term {
- private final Operator operator;
- private final List value = new ArrayList<>();
- public Expression(Operator operator) {
- this.operator = operator;
- }
- }
-
- public static final class Varspec {
- public final String varname;
- public final String decoded;
- public final int maxLength;
- public final boolean exploded;
- private Varspec(String varname, String decoded, int maxLength, boolean exploded) {
- this.varname = varname;
- this.decoded = decoded;
- this.maxLength = maxLength;
- this.exploded = exploded;
- }
- }
-
- public static class Parser {
-
- private UriTemplateImpl template;
- private Expression expression;
-
- public UriTemplateImpl parseURITemplate(String s) {
- template = new UriTemplateImpl();
- if (parseURITemplate(s, 0) != s.length()) {
- throw new IllegalArgumentException();
- }
- for (Term term : template.terms) {
- if (term instanceof Expression && ((Expression) term).operator == Operator.FUTURE) {
- throw new IllegalArgumentException("Invalid reserved operator");
- }
- }
- return template;
- }
-
- public int parseURITemplate(String s, int pos) {
- while (true) {
- int idx = parseLiterals(s, pos);
- if (idx > pos) {
- template.terms.add(new Literals(literals.toString()));
- pos = idx;
- } else {
- idx = parseExpression(s, pos);
- if (idx > pos) {
- pos = idx;
- } else {
- break;
- }
- }
- }
- return pos;
- }
-
- public int parseExpression(String s, int pos) {
- if (pos < s.length() && s.charAt(pos) == '{') {
- int idx = pos + 1;
- Operator operator;
- if (idx < s.length() && isOperator(s.charAt(idx))) {
- operator = mapping.get(s.charAt(idx));
- idx++;
- } else {
- operator = Operator.SIMPLE_STRING_EXPANSION;
- }
- expression = new Expression(operator);
- idx = parseVariableList(s, idx);
- if (idx < s.length() && s.charAt(idx) == '}') {
- pos = idx + 1;
- }
- if (template != null) {
- template.terms.add(expression);
- }
- expression = null;
- }
- return pos;
- }
+ private List extractTokens() {
+ List result = new ArrayList<>();
+ boolean toToken = false;
+ boolean maxChar = false;
+ boolean operator = true;
+ final StringBuilder token = new StringBuilder();
- private static boolean isALPHA(char ch) {
- return ('A' <= ch && ch <= 'Z')
- || ('a'<= ch && ch <= 'z');
- }
-
- private int digit;
-
- public int parseDIGIT(String s, int pos) {
- char c;
- if (pos < s.length() && isDIGIT(c = s.charAt(pos))) {
- digit = c - '0';
- pos++;
- }
- return pos;
- }
-
- private static boolean isDIGIT(char ch) {
- return ('0' <= ch && ch <= '9');
- }
-
- static boolean isHEXDIG(char ch) {
- return isDIGIT(ch) || ('A' <= ch && ch <= 'F') || ('a' <= ch && ch <= 'f');
- }
-
- private char pctEncoded;
-
- private int parsePctEncoded(String s, int pos) {
- byte[] buffer = new byte[0]; //
- while (pos + 2 < s.length() && s.charAt(pos) == '%' && isHEXDIG(s.charAt(pos + 1)) && isHEXDIG(s.charAt(pos + 2))) {
- buffer = Arrays.copyOf(buffer, buffer.length + 1);
- buffer[buffer.length - 1] = (byte)Integer.parseInt(s.substring(pos + 1, pos + 3), 16);
- pos += 3;
- ByteBuffer bb = ByteBuffer.wrap(buffer);
- CharsetDecoder dec = StandardCharsets.UTF_8.newDecoder();
- CharBuffer chars = CharBuffer.allocate(1);
- CoderResult result = dec.decode(bb, chars, true);
- if (result.isUnderflow()) {
- dec.flush(chars);
- chars.flip();
- pctEncoded = chars.charAt(0);
+ for (int i = 0; i < uri.length(); i++) {
+ char character = uri.charAt(i);
+ switch (character) {
+ case '{':
+ toToken = true;
+ operator = true;
+ maxChar = false;
+ token.setLength(0);
break;
- }
- }
- return pos;
- }
-
- private static boolean isUnreserved(char ch) {
- return isALPHA(ch) || isDIGIT(ch) || ch == '-' || ch == '.' || ch == '_' || ch == '~';
- }
-
- private static boolean isReserved(char ch) {
- return isGenDelims(ch) || isSubDelims(ch);
- }
-
- private static boolean isGenDelims(char ch) {
- return ch == ':' || ch == '/' || ch == '?' || ch == '#' || ch == '[' || ch == ']' || ch == '@';
- }
-
- private static boolean isSubDelims(char ch) {
- return ch == '!' || ch == '$' || ch == '&' || ch == '\'' || ch == '(' || ch == ')' || ch == '*' || ch == '+' || ch == ',' || ch == ';' || ch == '=';
- }
-
- private static boolean isIprivate(int cp) {
- return (0xE000 <= cp && cp <= 0xF8FF)
- || (0xF0000 <= cp && cp <= 0xFFFFD)
- || (0x100000 <= cp && cp <= 0x10FFFD);
- }
-
- private static boolean isUcschar(int cp) {
- return (0xA0 <= cp && cp <= 0xD7FF)
- || (0xF900 <= cp && cp <= 0xFDCF)
- || (0xFDF0 <= cp && cp <= 0xFFEF)
- || (0x10000 <= cp && cp <= 0x1FFFD)
- || (0x20000 <= cp && cp <= 0x2FFFD)
- || (0x30000 <= cp && cp <= 0x3FFFD)
- || (0x40000 <= cp && cp <= 0x4FFFD)
- || (0x50000 <= cp && cp <= 0x5FFFD)
- || (0x60000 <= cp && cp <= 0x6FFFD)
- || (0x70000 <= cp && cp <= 0x7FFFD)
- || (0x80000 <= cp && cp <= 0x8FFFD)
- || (0x90000 <= cp && cp <= 0x9FFFD)
- || (0xA0000 <= cp && cp <= 0xAFFFD)
- || (0xB0000 <= cp && cp <= 0xBFFFD)
- || (0xC0000 <= cp && cp <= 0xCFFFD)
- || (0xD0000 <= cp && cp <= 0xDFFFD)
- || (0xE1000 <= cp && cp <= 0xEFFFD);
- }
-
- public StringBuilder literals;
-
- public int parseLiterals(String s, int pos) {
- literals = new StringBuilder();
- while (pos < s.length() ) {
- char ch = s.charAt(pos);
- if (ch == 0x21
- || (0x23 <= ch && ch <= 0x24)
- || ch == 0x26
- || (0x28 <= ch && ch <= 0x3B)
- || ch == 0x3D
- || (0x3F <= ch && ch <= 0x5B)
- || ch == 0x5D
- || ch == 0x5F
- || (0x61 <= ch && ch <= 0x7A)
- || ch == 0x7E) {
- pos++;
- encodeChar(ch, CharSet.LITERALS, literals);
- } else {
- if (Character.isSurrogate(ch)) {
- if (pos + 1 >= s.length()) {
- throw new IllegalArgumentException();
- }
- int cp = s.codePointAt(pos);
- if (isUcschar(cp) || isIprivate(cp)) {
- pctEncode(s.substring(pos, pos + 2), literals);
- pos += 2;
- } else {
- break;
- }
- } else {
- int idx = parsePctEncoded(s, pos);
- if (idx == pos) {
- break;
- }
- // Directly insert as this is allowed
- literals.append(s, pos, idx);
- pos = idx;
- }
- }
- }
- return pos;
- }
-
- private static boolean isOperator(char ch) {
- return isOpLevel2(ch) || isOpLevel3(ch) || isOpReserve(ch);
- }
-
- private static boolean isOpLevel2(char ch) {
- return ch == '+' || ch == '#';
- }
-
- private static boolean isOpLevel3(char ch) {
- return ch == '.' || ch == '/' || ch == ';' || ch == '?' || ch == '&';
- }
-
- private static boolean isOpReserve(char ch) {
- return ch == '=' || ch == ',' || ch == '!' || ch == '@' || ch == '|';
- }
-
- public int parseVariableList(String s, int pos) {
- int idx = parseVarspec(s, pos);
- if (expression != null) {
- expression.value.add(varspec);
- }
- if (idx > pos) {
- pos = idx;
- while (pos < s.length() && s.charAt(pos) == ',' && (idx = parseVarspec(s, pos + 1)) > pos + 1) {
- if (expression != null) {
- expression.value.add(varspec);
+ case '}':
+ if (toToken) {
+ result.add(token.toString());
+ toToken = false;
+ token.setLength(0);
}
- pos = idx;
- }
- }
- return pos;
- }
-
- public Varspec varspec;
-
- public int parseVarspec(String s, int pos) {
- varspec = null;
- int idx = parseVarname(s, pos);
- if (idx > pos) {
- String varname = s.substring(pos, idx);
- pos = parseModifierLevel4(s, idx);
- varspec = new Varspec(varname, sb.toString(), maxLength, exploded);
- }
- return pos;
- }
-
- private StringBuilder sb;
-
- public int parseVarname(String s, int pos) {
- sb = new StringBuilder();
- int idx = parseVarchar(s, pos);
- while (idx > pos) {
- pos = idx;
- if (pos < s.length() && s.charAt(pos) == '.') {
- sb.append('.');
- int j = parseVarchar(s, pos + 1);
- if (j > pos + 1) {
- idx = j;
- }
- } else {
- idx = parseVarchar(s, pos);
- }
- }
- return idx;
- }
-
- private int parseVarchar(String s, int pos) {
- if (pos < s.length()) {
- char ch = s.charAt(pos);
- if (isALPHA(ch) || isDIGIT(ch) || ch == '_') {
- sb.append(ch);
- pos++;
- } else {
- int idx = parsePctEncoded(s, pos);
- if (idx > pos) {
- sb.append(pctEncoded);
- pos = idx;
+ break;
+ case ',':
+ if (toToken) {
+ result.add(token.toString());
+ operator = true;
+ token.setLength(0);
+ break;
}
- }
- }
- return pos;
- }
-
- private boolean exploded;
-
- public int parseModifierLevel4(String s, int pos) {
- exploded = false;
- maxLength = -1;
- int idx = parsePrefixModifier(s, pos);
- if (idx > pos) {
- pos = idx;
- } else if (pos < s.length() && isExplode(s.charAt(pos))) {
- exploded = true;
- pos++;
- }
- return pos;
- }
-
- public int parsePrefixModifier(String s, int pos) {
- if (pos < s.length() && s.charAt(pos) == ':') {
- int idx = parseMaxLength(s, pos + 1);
- if (idx > pos + 1) {
- pos = idx;
- }
- }
- return pos;
- }
-
- private int maxLength;
-
- public int parseMaxLength(String s, int pos) {
- if (pos < s.length()) {
- char ch = s.charAt(pos);
- if ('1' <= ch && ch <= '9') {
- pos++;
- maxLength = ch - '0';
- for (int i = 0;i < 3;i++) {
- if (parseDIGIT(s, pos) > pos) {
- maxLength = maxLength * 10 + digit;
- pos++;
+ // Intentional fall-through for commas outside the {}
+ default:
+ if (toToken) {
+ if (operator) {
+ switch (character) {
+ case '+':
+ case '#':
+ case '.':
+ case '/':
+ case ';':
+ case '?':
+ case '&':
+ break;
+ default:
+ token.append(character);
+ break;
+ }
+ operator = false;
+ } else if (maxChar) {
+ // just consume
+ } else {
+ if (character == ':') {
+ maxChar = true;
+ } else if (character == '*') {
+ // consume
+ } else {
+ token.append(character);
+ }
}
}
- }
- }
- return pos;
- }
-
- private static boolean isExplode(char ch) {
- return ch == '*';
- }
- }
-
- private final List terms = new ArrayList<>();
-
- @Override
- public String expandToString(Variables variables, ExpandOptions options) {
- return expandToString(variables, options.getAllowVariableMiss());
- }
-
- @Override
- public String expandToString(Variables variables) {
- return expandToString(variables, true);
- }
-
- private String expandToString(Variables variables, boolean allowVariableMiss) {
- StringBuilder sb = new StringBuilder();
- for (Term term : terms) {
- if (term instanceof Literals) {
- sb.append(((Literals)term).value);
- } else {
- Expression expression = (Expression) term;
- expression.operator.expand(expression.value, variables, allowVariableMiss, sb);
- }
- }
- return sb.toString();
- }
-
- private static void encodeString(String s, CharSet allowedSet, boolean allowPctEncoded, StringBuilder buff) {
- int i = 0;
- while (i < s.length()) {
- char ch = s.charAt(i++);
- if (Character.isSurrogate(ch)) {
- PCT_ENCODER.get().encodeChars(ch, s.charAt(i++), buff);
- } else if (allowPctEncoded && ch == '%' && i + 1 < s.length() && isHEXDIG(s.charAt(i)) && isHEXDIG(s.charAt(i + 1))) {
- buff.append(s, i - 1, i + 2);
- i+= 2;
- } else {
- encodeChar(ch, allowedSet, buff);
- }
- }
- }
-
- private static void encodeChar(char ch, CharSet allowedSet, StringBuilder buff) {
- if (allowedSet.contains(ch)) {
- buff.append(ch);
- } else {
- PCT_ENCODER.get().encodeChar(ch, buff);
- }
- }
-
- private static final ThreadLocal PCT_ENCODER = ThreadLocal.withInitial(PctEncoder::new);
-
- private static class PctEncoder {
-
- private final CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
- private final ByteBuffer byteBuffer = ByteBuffer.allocate(6);
- private final CharBuffer charBuffer = CharBuffer.allocate(2);
-
- void encodeChar(char ch, StringBuilder buff) {
- try {
- charBuffer.put(ch);
- charBuffer.flip();
- encode(buff);
- } finally {
- encoder.reset();
- byteBuffer.clear();
- charBuffer.clear();
- }
- }
-
- void encodeChars(char ch1, char ch2, StringBuilder buff) {
- try {
- charBuffer.put(ch1);
- charBuffer.put(ch2);
- charBuffer.flip();
- encode(buff);
- } finally {
- encoder.reset();
- byteBuffer.clear();
- charBuffer.clear();
- }
- }
-
- private void encode(StringBuilder buff) {
- CoderResult res = encoder.encode(charBuffer, byteBuffer, true);
- if (res.isUnderflow()) {
- int pos = byteBuffer.position();
- byteBuffer.flip();
- for (int i = 0;i < pos;i++) {
- byte b = byteBuffer.get(i);
- pctEncode(b, buff);
- }
+ break;
}
}
- }
-
- // This does need to be fast, since it's used when building a template
- private static void pctEncode(String s, StringBuilder buff) {
- byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
- for (byte b : bytes) {
- pctEncode(b, buff);
- }
- }
-
- private static void pctEncode(byte b, StringBuilder buff) {
- int high = (b & 0xF0) >> 4;
- int low = b & 0x0F;
- buff.append('%');
- buff.append(HEX_ALPHABET, high, high + 1);
- buff.append(HEX_ALPHABET, low, low + 1);
+ return result;
}
}
diff --git a/src/test/java/io/vertx/uritemplate/ExpansionTest.java b/src/test/java/io/vertx/uritemplate/ExpansionTest.java
index 77f0bb9..e05f429 100644
--- a/src/test/java/io/vertx/uritemplate/ExpansionTest.java
+++ b/src/test/java/io/vertx/uritemplate/ExpansionTest.java
@@ -77,7 +77,7 @@ public void testSimpleStringExpansion() {
assertEquals("one=1,two=2,three=3,comma=%2C", UriTemplate.of("{map*}").expandToString(variables));
assertEquals("", UriTemplate.of("{empty_map}").expandToString(variables));
assertEquals("", UriTemplate.of("{empty_map*}").expandToString(variables));
- assertExpansionFailure("{list:1}");
+ assertEquals("o,t,t", UriTemplate.of("{list:1}").expandToString(variables));
assertExpansionFailure("{map:1}");
}
diff --git a/src/test/java/io/vertx/uritemplate/UriTemplateTest.java b/src/test/java/io/vertx/uritemplate/UriTemplateTest.java
index 0255bad..2c12c13 100644
--- a/src/test/java/io/vertx/uritemplate/UriTemplateTest.java
+++ b/src/test/java/io/vertx/uritemplate/UriTemplateTest.java
@@ -18,107 +18,6 @@
public class UriTemplateTest {
- @Test
- public void testParseURITemplate() {
- new UriTemplateImpl.Parser().parseURITemplate("http://example.org/~{username}/");
- }
-
- @Test
- public void testParseLiteral() {
- UriTemplateImpl.Parser parser = new UriTemplateImpl.Parser();
- assertEquals(3, parser.parseLiterals("foo", 0));
- assertEquals(2, parser.parseLiterals("\ud83c\udf09", 0));
- assertEquals("%F0%9F%8C%89", parser.literals.toString());
- }
-
- @Test
- public void testParseExpression() {
- assertEquals(0, new UriTemplateImpl.Parser().parseExpression("{", 0));
- assertEquals(2, new UriTemplateImpl.Parser().parseExpression("{}", 0));
- assertEquals(3, new UriTemplateImpl.Parser().parseExpression("{A}", 0));
- assertEquals(5, new UriTemplateImpl.Parser().parseExpression("{A,B}", 0));
- assertEquals(0, new UriTemplateImpl.Parser().parseExpression("{A,}", 0));
- assertEquals(4, new UriTemplateImpl.Parser().parseExpression("{#A}", 0));
- }
-
- @Test
- public void testVariableList() {
- assertEquals(1, new UriTemplateImpl.Parser().parseVariableList("A", 0));
- assertEquals(2, new UriTemplateImpl.Parser().parseVariableList("AB", 0));
- assertEquals(4, new UriTemplateImpl.Parser().parseVariableList("AB,C", 0));
- assertEquals(4, new UriTemplateImpl.Parser().parseVariableList("AB,C,", 0));
- assertEquals(6, new UriTemplateImpl.Parser().parseVariableList("AB,C,D", 0));
- assertEquals(6, new UriTemplateImpl.Parser().parseVariableList("AB,C,D}", 0));
- assertEquals(8, new UriTemplateImpl.Parser().parseVariableList("AB,C,D:1.", 0));
- }
-
- @Test
- public void testParseVarspec() {
- UriTemplateImpl.Parser parser = new UriTemplateImpl.Parser();
- assertEquals(3, parser.parseVarspec("A:1", 0));
- assertEquals("A", parser.varspec.varname);
- assertEquals(1, parser.varspec.maxLength);
- assertEquals(4, parser.parseVarspec("A:12", 0));
- assertEquals("A", parser.varspec.varname);
- assertEquals(12, parser.varspec.maxLength);
- assertEquals(4, parser.parseVarspec("AB:1", 0));
- assertEquals("AB", parser.varspec.varname);
- assertEquals(1, parser.varspec.maxLength);
- assertEquals(5, parser.parseVarspec("A.B:1", 0));
- assertEquals("A.B", parser.varspec.varname);
- assertEquals(1, parser.varspec.maxLength);
- assertEquals(4, parser.parseVarspec("AB:1}", 0));
- assertEquals("AB", parser.varspec.varname);
- assertEquals(1, parser.varspec.maxLength);
- assertEquals(3, parser.parseVarspec("A:1.", 0));
- assertEquals("A", parser.varspec.varname);
- assertEquals(1, parser.varspec.maxLength);
- assertEquals(12, parser.parseVarspec("%2F%E2%82%AC", 0));
- assertEquals("%2F%E2%82%AC", parser.varspec.varname);
- assertEquals("/\u20AC", parser.varspec.decoded);
- assertEquals(-1, parser.varspec.maxLength);
- }
-
- @Test
- public void testParseVarchar() {
- assertEquals(1, new UriTemplateImpl.Parser().parseVarname("A", 0));
- assertEquals(2, new UriTemplateImpl.Parser().parseVarname("AB", 0));
- assertEquals(3, new UriTemplateImpl.Parser().parseVarname("A.B", 0));
- assertEquals(2, new UriTemplateImpl.Parser().parseVarname("AB}", 0));
- assertEquals(1, new UriTemplateImpl.Parser().parseVarname("A.", 0));
- }
-
- @Test
- public void testModifierLevel4() {
- assertEquals(0, new UriTemplateImpl.Parser().parseModifierLevel4(":0", 0));
- assertEquals(2, new UriTemplateImpl.Parser().parseModifierLevel4(":1", 0));
- assertEquals(3, new UriTemplateImpl.Parser().parseModifierLevel4(":12", 0));
- assertEquals(4, new UriTemplateImpl.Parser().parseModifierLevel4(":123", 0));
- assertEquals(5, new UriTemplateImpl.Parser().parseModifierLevel4(":1234", 0));
- assertEquals(5, new UriTemplateImpl.Parser().parseModifierLevel4(":12345", 0));
- assertEquals(1, new UriTemplateImpl.Parser().parseModifierLevel4("*", 0));
- }
-
- @Test
- public void testParsePrefix() {
- assertEquals(0, new UriTemplateImpl.Parser().parsePrefixModifier(":0", 0));
- assertEquals(2, new UriTemplateImpl.Parser().parsePrefixModifier(":1", 0));
- assertEquals(3, new UriTemplateImpl.Parser().parsePrefixModifier(":12", 0));
- assertEquals(4, new UriTemplateImpl.Parser().parsePrefixModifier(":123", 0));
- assertEquals(5, new UriTemplateImpl.Parser().parsePrefixModifier(":1234", 0));
- assertEquals(5, new UriTemplateImpl.Parser().parsePrefixModifier(":12345", 0));
- }
-
- @Test
- public void testParseMaxLength() {
- assertEquals(0, new UriTemplateImpl.Parser().parseMaxLength("0", 0));
- assertEquals(1, new UriTemplateImpl.Parser().parseMaxLength("1", 0));
- assertEquals(2, new UriTemplateImpl.Parser().parseMaxLength("12", 0));
- assertEquals(3, new UriTemplateImpl.Parser().parseMaxLength("123", 0));
- assertEquals(4, new UriTemplateImpl.Parser().parseMaxLength("1234", 0));
- assertEquals(4, new UriTemplateImpl.Parser().parseMaxLength("12345", 0));
- }
-
@Test
public void testOpReserved() {
assertInvalidTemplate("{!test}");
@@ -126,7 +25,8 @@ public void testOpReserved() {
private static void assertInvalidTemplate(String template) {
try {
- UriTemplate.of(template);
+ Variables vars = Variables.variables().set("test", "foo");
+ UriTemplate.of(template).expandToString(vars);
fail("Was expecting " + template + " to fail");
} catch (Exception ignore) {
// Expected