JavaXT
|
|
URL Classpackage javaxt.utils; import java.util.*; //****************************************************************************** //** URL Class //****************************************************************************** /** * Used to parse urls, extract querystring parameters, etc. Partial * implementation of the java.net.URL class. Provides a querystring parser * that is not part of the java.net.URL class. Can be used to parse non-http * URLs, including JDBC connection strings. * ******************************************************************************/ public class URL { private LinkedHashMap<String, List<String>> parameters; private LinkedHashMap<String, List<String>> extendedParameters; private String protocol; private String host; private Integer port; private String path; //************************************************************************** //** Constructor //************************************************************************** /** Creates a new instance of URL using a java.net.URL */ public URL(java.net.URL url){ this(url.toString()); } //************************************************************************** //** Constructor //************************************************************************** /** Creates a new instance of URL using string representing a url. */ public URL(String url){ url = url.trim(); parameters = new LinkedHashMap<>(); extendedParameters = new LinkedHashMap<>(); if (url.contains("://")){ protocol = url.substring(0,url.indexOf("://")); url = url.substring(url.indexOf("://") + 3); } else{ if (url.startsWith("jdbc")){ protocol = url.substring(0, url.indexOf(";")); url = url.substring(url.indexOf(";")+1); } } if (url.contains("?")){ String query = url.substring(url.indexOf("?")+1); url = url.substring(0, url.indexOf("?")); parameters = parseQueryString(query); } else{ //no query string, check for jdbc params int idx = url.indexOf(";"); if (idx>-1){ //found jdbc delimiter extendedParameters = parseJDBCParams(url.substring(idx+1)); url = url.substring(0, idx); } } if (url.contains("/")){ path = url.substring(url.indexOf("/")); url = url.substring(0, url.indexOf("/")); } if (url.contains(":")){ try{ port = Integer.valueOf(url.substring(url.indexOf(":")+1)); url = url.substring(0, url.indexOf(":")); } catch(Exception e){} } host = url; } //************************************************************************** //** exists //************************************************************************** /** Used to test whether the url endpoint exists. Currently only supports * HTTP URLs. */ public boolean exists(){ try{ java.net.URLConnection conn = new java.net.URL(this.toString()).openConnection(); conn.setConnectTimeout(5000); conn.getInputStream(); return true; } catch(Exception e){ //System.err.println(e.toString()); //System.err.println("URL not found: " + this.toString()); } return false; } //************************************************************************** //** parseQueryString //************************************************************************** /** Used to parse a url query string and create a list of name/value pairs. */ public static LinkedHashMap<String, List<String>> parseQueryString(String query){ //Create an empty hashmap LinkedHashMap<String, List<String>> parameters = new LinkedHashMap<>(); if (query==null) return parameters; query = query.trim(); if (query.length()==0) return parameters; //Parse the querystring, one character at a time if (query.startsWith("&")) query = query.substring(1); query += "&"; StringBuffer word = new StringBuffer(); for (int i=0; i<query.length(); i++){ String c = query.substring(i,i+1); if (c.equals("&")){ if (i+5<query.length() && query.substring(i,i+5).equals("&")){ word.append(c); } else{ int x = word.indexOf("="); if (x>=0){ String key = word.substring(0,x); String value = decode(word.substring(x+1)); List<String> values = getParameter(key, parameters); if (values==null) values = new LinkedList<>(); values.add(value); setParameter(key, values, parameters); } else{ setParameter(word.toString(), null, parameters); } word = new StringBuffer(); } } else{ word.append(c); } } return parameters; } //************************************************************************** //** parseJDBCParams //************************************************************************** /** Used to parse a JDBC parameter strings and return a list of name/value * pairs. */ public static LinkedHashMap<String, List<String>> parseJDBCParams(String params){ LinkedHashMap<String, List<String>> parameters = new LinkedHashMap<>(); final String[] pairs = params.split(";"); for (String pair : pairs) { int idx = pair.indexOf("="); String key = idx > 0 ? decode(pair.substring(0, idx)) : pair; String value = idx > 0 && pair.length() > idx + 1 ? decode(pair.substring(idx + 1)) : null; if (!parameters.containsKey(key)) parameters.put(key, new LinkedList<>()); parameters.get(key).add(value); } return parameters; } //************************************************************************** //** decode //************************************************************************** /** Used to decode a URL encoded string */ public static String decode(String str){ try{ //Replace unencoded "%" characters with "%25". The regex finds a "%" //but only those that are NOT followed by 2 hex characters (0-F), //then replaces with the ENCODED version of the % character "%25". //Credit: https://stackoverflow.com/a/18368345 str = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25"); //Decode the string with the URLDecoder if (str.contains("+")){ StringBuilder out = new StringBuilder(); while (str.contains("+")){ int idx = str.indexOf("+"); if (idx==0){ out.append("+"); str = str.substring(1); } else{ out.append(java.net.URLDecoder.decode(str.substring(0, idx), "UTF-8")); str = str.substring(idx); } } out.append(java.net.URLDecoder.decode(str, "UTF-8")); return out.toString(); } else{ return java.net.URLDecoder.decode(str, "UTF-8"); } } catch(Exception e){ return str; } } //************************************************************************** //** encode //************************************************************************** /** Used to URL encode a string */ public static String encode(String str){ try{ if (str.contains(" ")){ StringBuilder out = new StringBuilder(); while (str.contains(" ")){ int idx = str.indexOf(" "); if (idx==0){ out.append("%20"); str = str.substring(1); } else{ out.append(java.net.URLEncoder.encode(str.substring(0, idx), "UTF-8")); str = str.substring(idx); } } out.append(java.net.URLEncoder.encode(str, "UTF-8")); return out.toString(); } else{ return java.net.URLEncoder.encode(str, "UTF-8"); //.replaceAll("\\+", "%2B"); } } catch(Exception e){ return str; } } //************************************************************************** //** setParameter //************************************************************************** /** Used to set or update a value for a given parameter in the query string. * If append is true, the value will be added to other values for this key. */ public void setParameter(String key, String value, boolean append){ if (value!=null) value = decode(value); key = key.toLowerCase(); if (append){ List<String> values = getParameter(key, parameters); Iterator<String> it = values.iterator(); while(it.hasNext()){ if (it.next().equalsIgnoreCase(value)){ append = false; break; } } if (append) { values.add(value); setParameter(key, values, parameters); } } else{ if (value!=null){ List<String> values = new LinkedList<>(); values.add(value); setParameter(key, values, parameters); } } } //************************************************************************** //** setParameter //************************************************************************** /** Used to set or update a value for a given parameter. */ public void setParameter(String key, String value){ setParameter(key, value, false); } //************************************************************************** //** getParameter //************************************************************************** /** Returns the value of a specific variable supplied in the query string. * * @param key Query string parameter name. Performs a case insensitive * search for the keyword. * * @return Returns a comma delimited list of values associated with the * given key. Returns a zero length string if the key is not found or if * the value is null. */ public String getParameter(String key){ StringBuilder str = new StringBuilder(); List<String> values = getParameter(key, parameters); if (values!=null){ for (int i=0; i<values.size(); i++){ str.append(values.get(i)); if (i<values.size()-1) str.append(","); } return str.toString(); } else{ return ""; } } //************************************************************************** //** getParameter //************************************************************************** /** Returns the value of a specific variable supplied in the query string. * @param keys An array containing multiple possible parameter names. * Performs a case insensitive search for each parameter name and returns * the value for the first match. * * @return Returns a comma delimited list of values associated with the * given keys. Returns a zero length string if the key is not found or if * the value is null. */ public String getParameter(String[] keys){ StringBuilder str = new StringBuilder(); for (String key : keys){ List<String> values = getParameter(key.toLowerCase(), parameters); if (values!=null){ for (int i=0; i<values.size(); i++){ str.append(values.get(i) + ","); } } } String value = str.toString(); if (value.endsWith(",")) value = value.substring(0, value.length()-1); return value; } //************************************************************************** //** getParameters //************************************************************************** /** Returns a list of parameters found in query string. */ public LinkedHashMap<String, List<String>> getParameters(){ return parameters; } //************************************************************************** //** getExtendedParameters //************************************************************************** /** Returns a list of extended parameters (e.g. jdbc params) that are not * part of a standard url */ public LinkedHashMap<String, List<String>> getExtendedParameters(){ return extendedParameters; } //************************************************************************** //** removeParameter //************************************************************************** /** Used to remove a parameter from the query string */ public String removeParameter(String key){ StringBuilder str = new StringBuilder(); List<String> values = removeParameter(key, parameters); if (values!=null){ for (int i=0; i<values.size(); i++){ str.append(values.get(i)); if (i<values.size()-1) str.append(","); } return str.toString(); } else{ return ""; } } public static List<String> getParameter(String key, HashMap<String, List<String>> parameters){ List<String> values = parameters.get(key); if (values==null){ Iterator<String> it = parameters.keySet().iterator(); while (it.hasNext()){ String s = it.next(); if (s.equalsIgnoreCase(key)) return parameters.get(s); } } return values; }; public static void setParameter(String key, List<String> values, HashMap<String, List<String>> parameters){ Iterator<String> it = parameters.keySet().iterator(); while (it.hasNext()){ String s = it.next(); if (s.equalsIgnoreCase(key)){ parameters.put(key, values); return; } } parameters.put(key, values); } public static List<String> removeParameter(String key, HashMap<String, List<String>> parameters){ List<String> values = parameters.remove(key); if (values==null){ Iterator<String> it = parameters.keySet().iterator(); while (it.hasNext()){ String s = it.next(); if (s.equalsIgnoreCase(key)) return parameters.remove(s); } } return values; }; //************************************************************************** //** getHost //************************************************************************** /** Returns the host name or IP address found in the URL. */ public String getHost(){ return host; } //************************************************************************** //** setHost //************************************************************************** /** Used to update the host name or IP address found in the URL. */ public void setHost(String host){ if (host.contains(":")){ port = Integer.valueOf(host.substring(host.indexOf(":")+1)); host = host.substring(0, host.indexOf(":")); } this.host = host; } //************************************************************************** //** getPort //************************************************************************** /** Returns the server port found in the URL. */ public Integer getPort(){ return port; } //************************************************************************** //** setPort //************************************************************************** /** Used to update the port found in the URL. */ public void setPort(int port){ this.port = port; } //************************************************************************** //** setProtocol //************************************************************************** /** Used to update the protocol found in the URL. */ public void setProtocol(String protocol){ this.protocol = protocol; } //************************************************************************** //** getProtocol //************************************************************************** /** Returns the protocol found in the URL. */ public String getProtocol(){ return protocol; } //************************************************************************** //** getQueryString //************************************************************************** /** Returns the query string in the URL, or an empty string if none exists. */ public String getQueryString(){ StringBuilder str = new StringBuilder(); HashSet<String> keys = getKeys(); Iterator<String> it = keys.iterator(); while (it.hasNext()){ String key = it.next(); List<String> values = getParameter(key, parameters); if (values==null || values.isEmpty()){ str.append(encode(key)); } else{ int x = 0; for (String value : values){ if (x>0) str.append("&"); str.append(encode(key)); if (value!=null){ if (value.trim().length()>0){ str.append("="); boolean isEncoded = !decode(value).equals(value); str.append(isEncoded ? value : encode(value)); } } x++; } } if (it.hasNext()) str.append("&"); } return str.toString(); } //************************************************************************** //** setQueryString //************************************************************************** /** Used to update the query string in the URL. */ public void setQueryString(String query){ if (query==null){ parameters = new LinkedHashMap<>(); } else{ query = query.trim(); if (query.startsWith("?")) query = query.substring(1).trim(); if (query.length()>0){ parameters = parseQueryString(query); } } } //************************************************************************** //** getKeys //************************************************************************** /** Returns a list of parameter names found in the query string. */ public HashSet<String> getKeys(){ HashSet<String> keys = new HashSet<>(); Iterator<String> it = parameters.keySet().iterator(); while(it.hasNext()){ keys.add(it.next()); } return keys; } //************************************************************************** //** getPath //************************************************************************** /** Return the path portion of the URL, starting with a "/" character. The * path does not include the query string. If no path is found, returns a * null. */ public String getPath(){ return path; } //************************************************************************** //** setPath //************************************************************************** /** Used to update the path portion of the URL. If the supplied path starts * with "./" or "../", only part of the path will be replaced. Otherwise, * the entire path will be replaced. * <p/> * When supplying a relative path (path starting with "./" or "../"), the * url parser assumes that directories in the original path are terminated * with a "/". For example: * <pre>http://www.example.com/path/</pre> * If a path is not terminated with a "/", the parser assumes that the last * "/" separates a path from a file. Example: * <pre>http://www.example.com/path/file.html</pre> * For example, if the original url looks like this: * <pre>http://www.example.com/path/</pre> * If you provide a relative path like "../index.html", will yield this: * <pre>http://www.example.com/index.html</pre> * <p/> * Note that if the supplied path contains a query string, * the original query string will be replaced with the new one. */ public void setPath(String path){ if (path==null){ path = ""; } else { path = path.trim(); if (path.contains("?")){ String query = path.substring(path.indexOf("?")+1); path = path.substring(0, path.indexOf("?")); parameters = parseQueryString(query); } if (path.contains(";")){ //found jdbc delimiter path = path.substring(0, path.indexOf(";")); } if (!path.startsWith("/")){ if (path.startsWith("./") || path.startsWith("../")){ String RelPath = path; //Remove "./" prefix in the RelPath if (RelPath.length()>2){ if (RelPath.substring(0,2).equals("./")){ RelPath = RelPath.substring(2,RelPath.length()); } } //Build Path String urlPath = ""; String newPath = ""; if (RelPath.substring(0,1).equals("/")){ newPath = RelPath; } else{ urlPath = "/"; String dir = ""; String orgPath = getPath(); if (orgPath==null) orgPath = ""; if (orgPath.length()>1 && !orgPath.endsWith("/")){ orgPath = orgPath.substring(0, orgPath.lastIndexOf("/")); } String[] arr = orgPath.split("/"); String[] arrRelPath = RelPath.split("/"); for (int i=0; i<=(arr.length-arrRelPath.length); i++){ dir = arr[i]; if (dir.length()>0){ urlPath += dir + "/"; } } //This can be cleaned-up a bit... if (RelPath.substring(0,1).equals("/")){ newPath = RelPath.substring(1,RelPath.length()); } else if (RelPath.substring(0,2).equals("./")){ newPath = RelPath.substring(2,RelPath.length()); } else if (RelPath.substring(0,3).equals("../")){ newPath = RelPath.replace("../", ""); } else{ newPath = RelPath; } } //System.out.println("urlPath: " + urlPath); //System.out.println("newPath: " + newPath); path = urlPath + newPath; } else{ path = "/" + path; } } } this.path = path; } //************************************************************************** //** toString //************************************************************************** /** Returns the URL as a string. */ public String toString(){ //Update Host String host = this.host; if (port!=null && port>0) host += ":" + port; //Update Path String path = ""; if (getPath()!=null) path = getPath(); //Update Query String String query = getQueryString(); if (query.length()>0) query = "?" + query; //Assemble URL return protocol + "://" + host + path + query; } //************************************************************************** //** toURL //************************************************************************** /** Returns a properly encoded URL for HTTP requests */ public java.net.URL toURL(){ java.net.URL url = null; try{ Integer port = this.port; if (port==null){ if (protocol.equalsIgnoreCase("http")) port = 80; else if (protocol.equalsIgnoreCase("https")) port = 443; else if (protocol.equalsIgnoreCase("ftp")) port = 23; else{ try{ port = new java.net.URL(protocol + "://" + host).getPort(); } catch(Exception e){} } } url = new java.net.URI(protocol, null, host, port, path, null, null).toURL(); //Encode and append QueryString as needed String query = getQueryString(); if (query.length()>0){ url = new java.net.URL(url.toString() + "?" + query); } } catch(Exception e){ //e.printStackTrace(); } return url; } } |