/*
* Copyright 2004-2005 Revolution Systems Inc.
*
* 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
*
* http://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.
*/
package com.revolsys.util;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import com.revolsys.collection.map.LinkedHashMapEx;
import com.revolsys.collection.map.MapEx;
import com.revolsys.datatype.DataTypes;
import com.revolsys.io.FileUtil;
import com.revolsys.spring.resource.Resource;
/**
* The UrlUtil class is a utility class for processing and create URL strings.
*
* @author Paul Austin
*/
public final class UrlUtil {
private static final String TLD = "\\p{Alpha}+";
private static final String IP4_ADDRESS = "\\d{1,3}.\\d{1,3}.\\d{1,3}.\\d{1,3}";
private static final String DOMAIN_PART = "\\p{Alpha}[\\p{Alpha}0-9\\-]*\\.";
private static final String DOMAIN_NAME = "(?:" + DOMAIN_PART + ")+" + TLD;
private static final String DOMAIN = "(?:" + IP4_ADDRESS + "|" + DOMAIN_NAME + ")";
private static final String WORD_CHARACTERS = "a-zA-Z0-9\\+!#$%&'*+-/=?^_`{}|~";
private static final String LOCAL_PART = "[" + WORD_CHARACTERS + "][" + WORD_CHARACTERS + "\\.]*["
+ WORD_CHARACTERS + "]?";
private static final String EMAIL_RE = "^(" + LOCAL_PART + ")@(" + DOMAIN + ")$";
private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_RE);
public static void appendQuery(final StringBuilder query,
final Map<String, ? extends Object> parameters) throws Error {
if (parameters != null) {
boolean firstParameter = true;
for (final Entry<String, ? extends Object> parameter : parameters.entrySet()) {
final String name = parameter.getKey();
final Object value = parameter.getValue();
if (name != null && value != null) {
if (!firstParameter) {
query.append('&');
} else {
firstParameter = false;
}
try {
if (value instanceof String[]) {
final String[] values = (String[])value;
for (int i = 0; i < values.length; i++) {
query.append(name).append('=').append(URLEncoder.encode(values[i], "US-ASCII"));
if (i < values.length - 1) {
query.append('&');
}
}
} else if (value instanceof Collection) {
boolean first = true;
final Collection<?> values = (Collection<?>)value;
for (final Object childValue : values) {
if (childValue != null) {
if (first == true) {
first = false;
} else {
query.append('&');
}
query.append(name)
.append('=')
.append(URLEncoder.encode(childValue.toString(), "US-ASCII"));
}
}
} else {
query.append(name)
.append('=')
.append(URLEncoder.encode(value.toString(), "US-ASCII"));
}
} catch (final UnsupportedEncodingException e) {
throw new Error(e);
}
}
}
}
}
public static void appendQuery(final StringBuilder query, final String name, final Object value) {
final boolean firstParameter = query.length() == 0;
if (name != null && value != null) {
if (!firstParameter) {
query.append('&');
}
try {
if (value instanceof String[]) {
final String[] values = (String[])value;
for (int i = 0; i < values.length; i++) {
query.append(name).append('=').append(URLEncoder.encode(values[i], "US-ASCII"));
if (i < values.length - 1) {
query.append('&');
}
}
} else if (value instanceof Collection) {
boolean first = true;
final Collection<?> values = (Collection<?>)value;
for (final Object childValue : values) {
if (childValue != null) {
if (first == true) {
first = false;
} else {
query.append('&');
}
query.append(name)
.append('=')
.append(URLEncoder.encode(childValue.toString(), "US-ASCII"));
}
}
} else {
query.append(name).append('=').append(URLEncoder.encode(value.toString(), "US-ASCII"));
}
} catch (final UnsupportedEncodingException e) {
throw new Error(e);
}
}
}
/**
* Clean repeated // characters from the URL path.
*
* @param url
* @return
*/
public static String cleanPath(final String url) {
return url.replaceAll("/+", "/").replaceAll("^((\\w)+:)/", "$1//").replaceAll("^file://",
"file:///");
}
public static String getContent(final String urlString) {
try {
final URL url = UrlUtil.getUrl(urlString);
final InputStream in = url.openStream();
return FileUtil.getString(in);
} catch (final IOException e) {
throw new RuntimeException("Unable to read " + urlString, e);
}
}
public static String getFileBaseName(final URL url) {
final String name = getFileName(url);
final int dotIndex = name.lastIndexOf('.');
if (dotIndex != -1) {
return name.substring(0, dotIndex);
} else {
return name;
}
}
public static String getFileName(final String url) {
return getFileName(getUrl(url));
}
public static String getFileName(final URI url) {
final String path = url.getPath();
final int index = path.lastIndexOf('/');
if (index != -1) {
return path.substring(index + 1);
} else {
return path;
}
}
public static String getFileName(final URL url) {
final String path = url.getPath();
final int index = path.lastIndexOf('/');
if (index != -1) {
return path.substring(index + 1);
} else {
return path;
}
}
public static InputStream getInputStream(final String urlString) {
final URL url = getUrl(urlString);
return getInputStream(url);
}
public static InputStream getInputStream(final URL url) {
try {
return url.openStream();
} catch (final IOException e) {
throw new IllegalArgumentException("Cannot open stream for: " + url, e);
}
}
public static String getParent(final String urlString) {
final int index = urlString.lastIndexOf('/');
if (index != -1) {
final String parentPath = urlString.substring(0, index);
return parentPath;
} else {
return urlString;
}
}
public static URL getParent(final URL url) {
final String urlString = url.toString();
final int index = urlString.lastIndexOf('/');
if (index == -1) {
return url;
} else {
final String parentPath = urlString.substring(0, index + 1);
return getUrl(parentPath);
}
}
public static String getParentString(final URL url) {
final String urlString = url.toString();
return getParent(urlString);
}
/**
* Construct a new new URL from the baseUrl with the additional query string
* parameters.
*
* @param baseUrl The baseUrl.
* @param parameters The additional parameters to add to the query string.
* @return The new URL.
*/
public static String getQueryString(final Map<String, ? extends Object> parameters) {
final StringBuilder query = new StringBuilder();
appendQuery(query, parameters);
return query.toString();
}
@SuppressWarnings("unchecked")
public static Map<String, Object> getQueryStringMap(final String queryString) {
final MapEx map = new LinkedHashMapEx();
if (Property.hasValue(queryString)) {
for (final String part : queryString.split("\\&")) {
final int equalsIndex = part.indexOf("=");
if (equalsIndex > -1) {
final String name = part.substring(0, equalsIndex);
final String value = percentDecode(
part.substring(equalsIndex + 1).replaceAll("\\+", " "));
if (map.containsKey(name)) {
final Object existingValue = map.get(name);
if (existingValue instanceof List) {
final List<Object> list = (List<Object>)existingValue;
list.add(value);
} else {
final List<Object> list = new ArrayList<>();
list.add(existingValue);
list.add(value);
}
} else {
map.put(name, value);
}
}
}
}
return map;
}
public static URI getUri(final String uri) {
try {
return new URI(uri);
} catch (final URISyntaxException e) {
throw new IllegalArgumentException("Unknown URI: " + uri, e);
}
}
public static URI getUri(final URL url) {
try {
return url.toURI();
} catch (final URISyntaxException e) {
throw new IllegalArgumentException("Unknown URI: " + url, e);
}
}
/**
* Construct a new new URL from the baseUrl with the additional query string
* parameters.
*
* @param baseUrl The baseUrl.
* @param parameters The additional parameters to add to the query string.
* @return The new URL.
*/
public static String getUrl(final Object baseUrl,
final Map<String, ? extends Object> parameters) {
return getUrl(baseUrl.toString(), parameters);
}
public static URL getUrl(final String urlString) {
if (Property.isEmpty(urlString)) {
return null;
} else {
try {
return new URL(urlString);
} catch (final MalformedURLException e) {
throw new IllegalArgumentException("Unknown URL", e);
}
}
}
/**
* Construct a new new URL from the baseUrl with the additional query string
* parameters.
*
* @param baseUrl The baseUrl.
* @param parameters The additional parameters to add to the query string.
* @return The new URL.
*/
public static String getUrl(String baseUrl, final Map<String, ? extends Object> parameters) {
final int fragmentIndex = baseUrl.indexOf('#');
String fragment = null;
if (fragmentIndex > -1 && fragmentIndex < baseUrl.length() - 1) {
fragment = baseUrl.substring(fragmentIndex + 1);
baseUrl = baseUrl.substring(0, fragmentIndex);
}
final String query = getQueryString(parameters);
String url;
if (query.length() == 0) {
url = baseUrl;
} else {
final int qsIndex = baseUrl.indexOf('?');
if (qsIndex == baseUrl.length() - 1) {
url = baseUrl + query;
} else if (qsIndex > -1) {
url = baseUrl + '&' + query;
} else {
url = baseUrl + '?' + query;
}
}
if (Property.hasValue(fragment)) {
return url + "#" + fragment;
} else {
return url;
}
}
public static URL getUrl(final URL parent, final String child) {
if (parent == null) {
return null;
} else {
try {
// final String encodedChild = percentEncode(child);
final StringBuilder newUrl = new StringBuilder(parent.toExternalForm());
final String ref = parent.getRef();
if (ref != null) {
newUrl.setLength(newUrl.length() - ref.length() - 1);
}
final String query = parent.getQuery();
if (query != null) {
newUrl.setLength(newUrl.length() - query.length() - 1);
}
if (newUrl.charAt(newUrl.length() - 1) != '/') {
newUrl.append('/');
}
newUrl.append(child);
if (query != null) {
newUrl.append('?');
newUrl.append(query);
}
if (ref != null) {
newUrl.append('#');
newUrl.append(ref);
}
return new URL(newUrl.toString());
} catch (final MalformedURLException e) {
throw new IllegalArgumentException("Cannot create child URL for " + parent + " + " + child);
}
}
}
public static URL getUrl(final UrlProxy parent, final String child) {
if (parent == null) {
return null;
} else {
final URL parentUrl = parent.getUrl();
return getUrl(parentUrl, child);
}
}
public static boolean isValidEmail(final String email) {
return EMAIL_PATTERN.matcher(email).matches();
}
public static Map<String, String> parseMatrixParams(final String matrixParams) {
final Map<String, String> params = new LinkedHashMap<>();
parseMatrixParams(matrixParams, params);
return params;
}
public static void parseMatrixParams(final String matrixParams,
final Map<String, String> params) {
for (final String param : matrixParams.split(";")) {
final String[] paramParts = param.split("=");
final String key = paramParts[0];
if (paramParts.length == 1) {
params.put(key, null);
} else {
final String value = paramParts[1];
params.put(key, value);
}
}
}
public static String percentDecode(final String encodedText) {
final int len = encodedText.length();
final StringBuilder decoded = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char ch = encodedText.charAt(i);
if (ch == '%') {
final String hex = encodedText.substring(i + 1, i + 3);
ch = (char)Integer.parseInt(hex, 16);
i += 2;
}
decoded.append(ch);
}
return decoded.toString();
}
public static String percentEncode(final String text) {
final int len = text.length();
final StringBuilder encoded = new StringBuilder(len);
for (int i = 0; i < len; i++) {
final char ch = text.charAt(i);
if (ch >= 'A' && ch <= 'Z' || //
ch >= 'a' && ch <= 'z' || //
ch >= '0' && ch <= '9' || //
ch == '-' || ch == '_' || ch == '.' || ch == '~' || //
ch == '?' || ch == '/' //
) {
encoded.append(ch);
} else {
encoded.append('%');
if (ch < 0x10) {
encoded.append('0');
}
encoded.append(Integer.toHexString(ch));
}
}
return encoded.toString();
}
public static File toFile(final URL url) {
try {
final URI uri = url.toURI();
final Path path = Paths.get(uri);
return path.toFile();
} catch (final URISyntaxException e) {
throw Exceptions.wrap(e);
}
}
public static URI toUri(final Object value) {
try {
if (value == null) {
return null;
} else if (value instanceof URL) {
final URL url = (URL)value;
return url.toURI();
} else if (value instanceof Resource) {
final Resource resource = (Resource)value;
return resource.getURI();
} else if (value instanceof File) {
final File file = (File)value;
return file.toURI();
} else if (value instanceof Path) {
final Path path = (Path)value;
return path.toUri();
} else {
final String string = DataTypes.toString(value);
return getUri(string);
}
} catch (final Throwable e) {
throw Exceptions.wrap(e);
}
}
public static URL toUrl(final Object value) {
if (value == null) {
return null;
} else if (value instanceof URL) {
return (URL)value;
} else if (value instanceof Resource) {
final Resource resource = (Resource)value;
return resource.getURL();
} else if (value instanceof File) {
final File file = (File)value;
return FileUtil.toUrl(file);
} else if (value instanceof Path) {
final Path path = (Path)value;
return com.revolsys.io.file.Paths.toUrl(path);
} else {
final String string = DataTypes.toString(value);
return UrlUtil.getUrl(string);
}
}
/**
* Construct a new UrlUtil.
*/
private UrlUtil() {
}
}