package org.neo4j.smack.routing;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.neo4j.smack.pipeline.http.HttpTokens;
// Modified verion of Nettys QueryStringDecoder,
// lets you reuse the same decoder.
// Can probably be optimized further..
public class ResettableQueryStringDecoder {
private Charset charset;
private String uri;
private String path;
private Map<String, List<String>> params;
/**
* Returns the decoded path string of the URI.
*/
public String getPath() {
if (path == null) {
int pathEndPos = uri.indexOf('?');
if (pathEndPos < 0) {
path = uri;
}
else {
return path = uri.substring(0, pathEndPos);
}
}
return path;
}
/**
* Returns the decoded key-value parameter pairs of the URI.
*/
public Map<String, List<String>> getParameters() {
if (params == null) {
int pathLength = getPath().length();
if (uri.length() == pathLength) {
return Collections.emptyMap();
}
params = decodeParams(uri.substring(pathLength + 1));
}
return params;
}
public void resetWith(String uri) {
reset(uri, HttpTokens.DEFAULT_CHARSET);
}
/**
* Creates a new decoder that decodes the specified URI encoded in the
* specified charset.
*/
public void reset(String uri, Charset charset) {
if (uri == null) {
throw new NullPointerException("uri");
}
if (charset == null) {
throw new NullPointerException("charset");
}
this.uri = uri;
this.charset = charset;
this.params = null;
this.path = null;
}
private Map<String, List<String>> decodeParams(String s) {
Map<String, List<String>> params = new LinkedHashMap<String, List<String>>();
String name = null;
int pos = 0; // Beginning of the unprocessed region
int i; // End of the unprocessed region
char c = 0; // Current character
for (i = 0; i < s.length(); i++) {
c = s.charAt(i);
if (c == '=' && name == null) {
if (pos != i) {
name = decodeComponent(s.substring(pos, i), charset);
}
pos = i + 1;
} else if (c == '&') {
if (name == null && pos != i) {
// We haven't seen an `=' so far but moved forward.
// Must be a param of the form '&a&' so add it with
// an empty value.
addParam(params, decodeComponent(s.substring(pos, i), charset), "");
} else if (name != null) {
addParam(params, name, decodeComponent(s.substring(pos, i), charset));
name = null;
}
pos = i + 1;
}
}
if (pos != i) { // Are there characters we haven't dealt with?
if (name == null) { // Yes and we haven't seen any `='.
addParam(params, decodeComponent(s.substring(pos, i), charset), "");
} else { // Yes and this must be the last value.
addParam(params, name, decodeComponent(s.substring(pos, i), charset));
}
} else if (name != null) { // Have we seen a name without value?
addParam(params, name, "");
}
return params;
}
private String decodeComponent(String s, Charset charset) {
if (s == null) {
return "";
}
try {
return URLDecoder.decode(s, charset.name());
} catch (UnsupportedEncodingException e) {
throw new UnsupportedCharsetException(charset.name());
}
}
private void addParam(Map<String, List<String>> params, String name, String value) {
List<String> values = params.get(name);
if (values == null) {
values = new ArrayList<String>(1); // Often there's only 1 value.
params.put(name, values);
}
values.add(value);
}
}