Skip to content

Instantly share code, notes, and snippets.

@emilianbold
Created October 17, 2019 18:11
Show Gist options
  • Save emilianbold/1752e587d81e23b9a9e23b2c71524c23 to your computer and use it in GitHub Desktop.
Save emilianbold/1752e587d81e23b9a9e23b2c71524c23 to your computer and use it in GitHub Desktop.
Java: encode URL query parameters
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//From https://github.com/spring-projects/spring-framework HierarchicalUriComponents
package uriencoder;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
public class SpringUtil {
public static String encodeUriComponent(String source, Charset charset, Type type) {
if (!hasLength(source)) {
return source;
}
//Assert.notNull(charset, "Charset must not be null");
//Assert.notNull(type, "Type must not be null");
byte[] bytes = source.getBytes(charset);
ByteArrayOutputStream bos = new ByteArrayOutputStream(bytes.length);
boolean changed = false;
for (byte b : bytes) {
if (b < 0) {
b += 256;
}
if (type.isAllowed(b)) {
bos.write(b);
}
else {
bos.write('%');
char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
bos.write(hex1);
bos.write(hex2);
changed = true;
}
}
return (changed ? new String(bos.toByteArray(), charset) : source);
}
/**
* Enumeration used to identify the allowed characters per URI component.
* <p>Contains methods to indicate whether a given character is valid in a specific URI component.
* @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>
*/
public enum Type {
SCHEME {
@Override
public boolean isAllowed(int c) {
return isAlpha(c) || isDigit(c) || '+' == c || '-' == c || '.' == c;
}
},
AUTHORITY {
@Override
public boolean isAllowed(int c) {
return isUnreserved(c) || isSubDelimiter(c) || ':' == c || '@' == c;
}
},
USER_INFO {
@Override
public boolean isAllowed(int c) {
return isUnreserved(c) || isSubDelimiter(c) || ':' == c;
}
},
HOST_IPV4 {
@Override
public boolean isAllowed(int c) {
return isUnreserved(c) || isSubDelimiter(c);
}
},
HOST_IPV6 {
@Override
public boolean isAllowed(int c) {
return isUnreserved(c) || isSubDelimiter(c) || '[' == c || ']' == c || ':' == c;
}
},
PORT {
@Override
public boolean isAllowed(int c) {
return isDigit(c);
}
},
PATH {
@Override
public boolean isAllowed(int c) {
return isPchar(c) || '/' == c;
}
},
PATH_SEGMENT {
@Override
public boolean isAllowed(int c) {
return isPchar(c);
}
},
QUERY {
@Override
public boolean isAllowed(int c) {
return isPchar(c) || '/' == c || '?' == c;
}
},
QUERY_PARAM {
@Override
public boolean isAllowed(int c) {
if ('=' == c || '&' == c) {
return false;
}
else {
return isPchar(c) || '/' == c || '?' == c;
}
}
},
FRAGMENT {
@Override
public boolean isAllowed(int c) {
return isPchar(c) || '/' == c || '?' == c;
}
},
URI {
@Override
public boolean isAllowed(int c) {
return isUnreserved(c);
}
};
/**
* Indicates whether the given character is allowed in this URI component.
* @return {@code true} if the character is allowed; {@code false} otherwise
*/
public abstract boolean isAllowed(int c);
/**
* Indicates whether the given character is in the {@code ALPHA} set.
* @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
*/
protected boolean isAlpha(int c) {
return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z');
}
/**
* Indicates whether the given character is in the {@code DIGIT} set.
* @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
*/
protected boolean isDigit(int c) {
return (c >= '0' && c <= '9');
}
/**
* Indicates whether the given character is in the {@code gen-delims} set.
* @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
*/
protected boolean isGenericDelimiter(int c) {
return (':' == c || '/' == c || '?' == c || '#' == c || '[' == c || ']' == c || '@' == c);
}
/**
* Indicates whether the given character is in the {@code sub-delims} set.
* @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
*/
protected boolean isSubDelimiter(int c) {
return ('!' == c || '$' == c || '&' == c || '\'' == c || '(' == c || ')' == c || '*' == c || '+' == c ||
',' == c || ';' == c || '=' == c);
}
/**
* Indicates whether the given character is in the {@code reserved} set.
* @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
*/
protected boolean isReserved(int c) {
return (isGenericDelimiter(c) || isSubDelimiter(c));
}
/**
* Indicates whether the given character is in the {@code unreserved} set.
* @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
*/
protected boolean isUnreserved(int c) {
return (isAlpha(c) || isDigit(c) || '-' == c || '.' == c || '_' == c || '~' == c);
}
/**
* Indicates whether the given character is in the {@code pchar} set.
* @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a>
*/
protected boolean isPchar(int c) {
return (isUnreserved(c) || isSubDelimiter(c) || ':' == c || '@' == c);
}
}
//from SpringFramework's StringUtil:
public static boolean hasLength(CharSequence str) {
return (str != null && str.length() > 0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment