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