001package com.nimbusds.oauth2.sdk.util;
002
003
004import java.io.UnsupportedEncodingException;
005import java.net.MalformedURLException;
006import java.net.URL;
007import java.net.URLDecoder;
008import java.net.URLEncoder;
009import java.util.HashMap;
010import java.util.Iterator;
011import java.util.Map;
012import java.util.StringTokenizer;
013
014
015/**
016 * URL operations.
017 *
018 * @author Vladimir Dzhuvinov
019 */
020public class URLUtils {
021
022        
023        /**
024         * The default UTF-8 character set.
025         */
026        public static final String CHARSET = "utf-8";
027        
028        
029        /**
030         * Gets the base part (protocol, host, port and path) of the specified
031         * URL.
032         *
033         * @param url The URL. May be {@code null}.
034         *
035         * @return The base part of the URL, {@code null} if the original URL 
036         *         is {@code null} or doesn't specify a protocol.
037         */
038        public static URL getBaseURL(final URL url) {
039        
040                if (url == null)
041                        return null;
042                
043                try {
044                        return new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath());
045                        
046                } catch (MalformedURLException e) {
047                
048                        return null;
049                }
050        }
051        
052        
053        /**
054         * Serialises the specified map of parameters into a URL query string. 
055         * The parameter keys and values are 
056         * {@code application/x-www-form-urlencoded} encoded.
057         *
058         * <p>Note that the '?' character preceding the query string in GET
059         * requests is not included in the returned string.
060         *
061         * <p>Example query string:
062         *
063         * <pre>
064         * response_type=code
065         * &amp;client_id=s6BhdRkqt3
066         * &amp;state=xyz
067         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
068         * </pre>
069         *
070         * <p>The opposite method is {@link #parseParameters}.
071         *
072         * @param params A map of the URL query parameters. May be empty or
073         *               {@code null}.
074         *
075         * @return The serialised URL query string, empty if no parameters.
076         */
077        public static String serializeParameters(final Map<String,String> params) {
078        
079                if (params == null || params.isEmpty())
080                        return "";
081                
082                StringBuilder sb = new StringBuilder();
083                
084                Iterator <Map.Entry<String,String>> it = params.entrySet().iterator();
085                
086                while (it.hasNext()) {
087                
088                        Map.Entry<String,String> entry = it.next();
089                        
090                        if (entry.getKey() == null || entry.getValue() == null)
091                                continue;
092                        
093                        try {
094                                String encodedKey = URLEncoder.encode(entry.getKey(), CHARSET);
095                                String encodedValue = URLEncoder.encode(entry.getValue(), CHARSET);
096                                
097                                if (sb.length() > 0)
098                                        sb.append('&');
099                                
100                                sb.append(encodedKey);
101                                sb.append('=');
102                                sb.append(encodedValue);
103        
104                        } catch (UnsupportedEncodingException e) {
105                                                  
106                                // UTF-8 should always be supported
107                        }
108                }
109                
110                return sb.toString();
111        }
112
113
114        /**
115         * Parses the specified URL query string into a parameter map. If a 
116         * parameter has multiple values only the first one will be saved. The
117         * parameter keys and values are 
118         * {@code application/x-www-form-urlencoded} decoded.
119         *
120         * <p>Note that the '?' character preceding the query string in GET
121         * requests must not be included.
122         *
123         * <p>Example query string:
124         *
125         * <pre>
126         * response_type=code
127         * &amp;client_id=s6BhdRkqt3
128         * &amp;state=xyz
129         * &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
130         * </pre>
131         *
132         * <p>The opposite method {@link #serializeParameters}.
133         *
134         * @param query The URL query string to parse. May be {@code null}.
135         *
136         * @return A map of the URL query parameters, empty if none are found.
137         */
138        public static Map<String,String> parseParameters(final String query) {
139                
140                Map<String,String> params = new HashMap<String, String>();
141                
142                if (query == null)
143                        return params; // empty map
144                
145                try {
146                        StringTokenizer st = new StringTokenizer(query, "&");
147
148                        while(st.hasMoreTokens()) {
149
150                                String param = st.nextToken();
151
152                                String pair[] = param.split("=");
153
154                                String key = URLDecoder.decode(pair[0], CHARSET);
155                                
156                                // Save the first value only
157                                if (params.containsKey(key))
158                                        continue;
159
160                                String value = "";
161
162                                if (pair.length > 1)
163                                        value = URLDecoder.decode(pair[1], CHARSET);
164                                
165                                params.put(key, value);
166                        }
167                        
168                } catch (UnsupportedEncodingException e) {
169                        
170                        // UTF-8 should always be supported
171                }
172                
173                return params;
174        }
175
176
177        /**
178         * Prevents instantiation.
179         */
180        private URLUtils() {
181        
182                // do nothing
183        }
184}