001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.activemq.util;
018    
019    import java.io.UnsupportedEncodingException;
020    import java.net.URI;
021    import java.net.URISyntaxException;
022    import java.net.URLDecoder;
023    import java.net.URLEncoder;
024    import java.util.ArrayList;
025    import java.util.Collections;
026    import java.util.HashMap;
027    import java.util.Iterator;
028    import java.util.List;
029    import java.util.Map;
030    
031    /**
032     * 
033     */
034    public class URISupport {
035    
036        public static class CompositeData {
037            private String host;
038            private String scheme;
039            private String path;
040            private URI components[];
041            private Map<String, String> parameters;
042            private String fragment;
043    
044            public URI[] getComponents() {
045                return components;
046            }
047    
048            public String getFragment() {
049                return fragment;
050            }
051    
052            public Map<String, String> getParameters() {
053                return parameters;
054            }
055    
056            public String getScheme() {
057                return scheme;
058            }
059    
060            public String getPath() {
061                return path;
062            }
063    
064            public String getHost() {
065                return host;
066            }
067    
068            public URI toURI() throws URISyntaxException {
069                StringBuffer sb = new StringBuffer();
070                if (scheme != null) {
071                    sb.append(scheme);
072                    sb.append(':');
073                }
074    
075                if (host != null && host.length() != 0) {
076                    sb.append(host);
077                } else {
078                    sb.append('(');
079                    for (int i = 0; i < components.length; i++) {
080                        if (i != 0) {
081                            sb.append(',');
082                        }
083                        sb.append(components[i].toString());
084                    }
085                    sb.append(')');
086                }
087    
088                if (path != null) {
089                    sb.append('/');
090                    sb.append(path);
091                }
092                if (!parameters.isEmpty()) {
093                    sb.append("?");
094                    sb.append(createQueryString(parameters));
095                }
096                if (fragment != null) {
097                    sb.append("#");
098                    sb.append(fragment);
099                }
100                return new URI(sb.toString());
101            }
102        }
103    
104        public static Map<String, String> parseQuery(String uri) throws URISyntaxException {
105            try {
106                uri = uri.substring(uri.lastIndexOf("?") + 1); // get only the relevant part of the query
107                Map<String, String> rc = new HashMap<String, String>();
108                if (uri != null) {
109                    String[] parameters = uri.split("&");
110                    for (int i = 0; i < parameters.length; i++) {
111                        int p = parameters[i].indexOf("=");
112                        if (p >= 0) {
113                            String name = URLDecoder.decode(parameters[i].substring(0, p), "UTF-8");
114                            String value = URLDecoder.decode(parameters[i].substring(p + 1), "UTF-8");
115                            rc.put(name, value);
116                        } else {
117                            rc.put(parameters[i], null);
118                        }
119                    }
120                }
121                return rc;
122            } catch (UnsupportedEncodingException e) {
123                throw (URISyntaxException)new URISyntaxException(e.toString(), "Invalid encoding").initCause(e);
124            }
125        }
126    
127        public static Map<String, String> parseParameters(URI uri) throws URISyntaxException {
128            if (!isCompositeURI(uri)) {
129                return uri.getQuery() == null ? emptyMap() : parseQuery(stripPrefix(uri.getQuery(), "?"));
130            } else {
131                CompositeData data = URISupport.parseComposite(uri);
132                Map<String, String> parameters = new HashMap<String, String>();
133                parameters.putAll(data.getParameters());
134                if (parameters.isEmpty()) {
135                    parameters = emptyMap();
136                }
137                
138                return parameters;
139            }
140        }
141    
142        public static URI applyParameters(URI uri, Map<String, String> queryParameters) throws URISyntaxException {
143            return applyParameters(uri, queryParameters, "");
144        }
145    
146        public static URI applyParameters(URI uri, Map<String, String> queryParameters, String optionPrefix) throws URISyntaxException {
147            if (queryParameters != null && !queryParameters.isEmpty()) {
148                StringBuffer newQuery = uri.getRawQuery() != null ? new StringBuffer(uri.getRawQuery()) : new StringBuffer() ;
149                for ( Map.Entry<String, String> param: queryParameters.entrySet()) {
150                    if (param.getKey().startsWith(optionPrefix)) {
151                        if (newQuery.length()!=0) {
152                            newQuery.append('&');
153                        }
154                        final String key = param.getKey().substring(optionPrefix.length());
155                        newQuery.append(key).append('=').append(param.getValue());
156                    }
157                }
158                uri = createURIWithQuery(uri, newQuery.toString());
159            }
160            return uri;
161        }
162        
163        @SuppressWarnings("unchecked")
164        private static Map<String, String> emptyMap() {
165            return Collections.EMPTY_MAP;
166        }
167    
168        /**
169         * Removes any URI query from the given uri
170         */
171        public static URI removeQuery(URI uri) throws URISyntaxException {
172            return createURIWithQuery(uri, null);
173        }
174    
175        /**
176         * Creates a URI with the given query
177         */
178        public static URI createURIWithQuery(URI uri, String query) throws URISyntaxException {
179            String schemeSpecificPart = uri.getRawSchemeSpecificPart();
180            // strip existing query if any
181            int questionMark = schemeSpecificPart.lastIndexOf("?");
182            // make sure question mark is not within parentheses
183            if (questionMark < schemeSpecificPart.lastIndexOf(")")) {
184                    questionMark = -1;
185            }
186            if (questionMark > 0) {
187                schemeSpecificPart = schemeSpecificPart.substring(0, questionMark);
188            }
189            if (query != null && query.length() > 0) {
190                schemeSpecificPart += "?" + query;
191            }
192            return new URI(uri.getScheme(), schemeSpecificPart, uri.getFragment());
193        }
194    
195        public static CompositeData parseComposite(URI uri) throws URISyntaxException {
196    
197            CompositeData rc = new CompositeData();
198            rc.scheme = uri.getScheme();
199            String ssp = stripPrefix(uri.getRawSchemeSpecificPart().trim(), "//").trim();
200            
201    
202            parseComposite(uri, rc, ssp);
203    
204            rc.fragment = uri.getFragment();
205            return rc;
206        }
207        
208        public static boolean isCompositeURI(URI uri) {
209            if (uri.getQuery() != null) {
210                return false;
211            } else {
212                String ssp = stripPrefix(uri.getRawSchemeSpecificPart().trim(), "(").trim();
213                ssp = stripPrefix(ssp, "//").trim();
214                try {
215                    new URI(ssp);
216                } catch (URISyntaxException e) {
217                    return false;
218                }
219                return true;
220            }
221        }
222    
223        /**
224         * @param uri
225         * @param rc
226         * @param ssp
227         * @throws URISyntaxException
228         */
229        private static void parseComposite(URI uri, CompositeData rc, String ssp) throws URISyntaxException {
230            String componentString;
231            String params;
232    
233            if (!checkParenthesis(ssp)) {
234                throw new URISyntaxException(uri.toString(), "Not a matching number of '(' and ')' parenthesis");
235            }
236    
237            int p;
238            int intialParen = ssp.indexOf("(");
239            if (intialParen == 0) {
240                rc.host = ssp.substring(0, intialParen);
241                p = rc.host.indexOf("/");
242                if (p >= 0) {
243                    rc.path = rc.host.substring(p);
244                    rc.host = rc.host.substring(0, p);
245                }
246                p = ssp.lastIndexOf(")");
247                componentString = ssp.substring(intialParen + 1, p);
248                params = ssp.substring(p + 1).trim();
249    
250            } else {
251                componentString = ssp;
252                params = "";
253            }
254    
255            String components[] = splitComponents(componentString);
256            rc.components = new URI[components.length];
257            for (int i = 0; i < components.length; i++) {
258                rc.components[i] = new URI(components[i].trim());
259            }
260    
261            p = params.indexOf("?");
262            if (p >= 0) {
263                if (p > 0) {
264                    rc.path = stripPrefix(params.substring(0, p), "/");
265                }
266                rc.parameters = parseQuery(params.substring(p + 1));
267            } else {
268                if (params.length() > 0) {
269                    rc.path = stripPrefix(params, "/");
270                }
271                rc.parameters = emptyMap();
272            }
273        }
274    
275        /**
276         * @param str
277         * @return
278         */
279        private static String[] splitComponents(String str) {
280            List<String> l = new ArrayList<String>();
281    
282            int last = 0;
283            int depth = 0;
284            char chars[] = str.toCharArray();
285            for (int i = 0; i < chars.length; i++) {
286                switch (chars[i]) {
287                case '(':
288                    depth++;
289                    break;
290                case ')':
291                    depth--;
292                    break;
293                case ',':
294                    if (depth == 0) {
295                        String s = str.substring(last, i);
296                        l.add(s);
297                        last = i + 1;
298                    }
299                    break;
300                default:
301                }
302            }
303    
304            String s = str.substring(last);
305            if (s.length() != 0) {
306                l.add(s);
307            }
308    
309            String rc[] = new String[l.size()];
310            l.toArray(rc);
311            return rc;
312        }
313    
314        public static String stripPrefix(String value, String prefix) {
315            if (value.startsWith(prefix)) {
316                return value.substring(prefix.length());
317            }
318            return value;
319        }
320    
321        public static URI stripScheme(URI uri) throws URISyntaxException {
322            return new URI(stripPrefix(uri.getSchemeSpecificPart().trim(), "//"));
323        }
324    
325        public static String createQueryString(Map options) throws URISyntaxException {
326            try {
327                if (options.size() > 0) {
328                    StringBuffer rc = new StringBuffer();
329                    boolean first = true;
330                    for (Iterator iter = options.keySet().iterator(); iter.hasNext();) {
331                        if (first) {
332                            first = false;
333                        } else {
334                            rc.append("&");
335                        }
336                        String key = (String)iter.next();
337                        String value = (String)options.get(key);
338                        rc.append(URLEncoder.encode(key, "UTF-8"));
339                        rc.append("=");
340                        rc.append(URLEncoder.encode(value, "UTF-8"));
341                    }
342                    return rc.toString();
343                } else {
344                    return "";
345                }
346            } catch (UnsupportedEncodingException e) {
347                throw (URISyntaxException)new URISyntaxException(e.toString(), "Invalid encoding").initCause(e);
348            }
349        }
350    
351        /**
352         * Creates a URI from the original URI and the remaining paramaters
353         * 
354         * @throws URISyntaxException
355         */
356        public static URI createRemainingURI(URI originalURI, Map params) throws URISyntaxException {
357            String s = createQueryString(params);
358            if (s.length() == 0) {
359                s = null;
360            }
361            return createURIWithQuery(originalURI, s);
362        }
363    
364        public static URI changeScheme(URI bindAddr, String scheme) throws URISyntaxException {
365            return new URI(scheme, bindAddr.getUserInfo(), bindAddr.getHost(), bindAddr.getPort(), bindAddr
366                .getPath(), bindAddr.getQuery(), bindAddr.getFragment());
367        }
368    
369        public static boolean checkParenthesis(String str) {
370            boolean result = true;
371            if (str != null) {
372                int open = 0;
373                int closed = 0;
374    
375                int i = 0;
376                while ((i = str.indexOf('(', i)) >= 0) {
377                    i++;
378                    open++;
379                }
380                i = 0;
381                while ((i = str.indexOf(')', i)) >= 0) {
382                    i++;
383                    closed++;
384                }
385                result = open == closed;
386            }
387            return result;
388        }
389    
390        public int indexOfParenthesisMatch(String str) {
391            int result = -1;
392    
393            return result;
394        }
395    
396    }