View Javadoc

1   /*
2    * Copyright 2003-2008 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * You are receiving this code free of charge, which represents many hours of
17   * effort from other individuals and corporations.  As a responsible member 
18   * of the community, you are asked (but not required) to donate any 
19   * enhancements or improvements back to the community under a similar open 
20   * source license.  Thank you. -TMN
21   */
22  package groovyx.net.http;
23  
24  import java.net.MalformedURLException;
25  import java.net.URI;
26  import java.net.URISyntaxException;
27  import java.net.URL;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  
33  import org.apache.http.NameValuePair;
34  import org.apache.http.client.utils.URLEncodedUtils;
35  import org.apache.http.message.BasicNameValuePair;
36  
37  /**
38   * This class implements a mutable URI.  All <code>set</code>, <code>add</code> 
39   * and <code>remove</code> methods affect this class' internal URI 
40   * representation.  All mutator methods support chaining, e.g.
41   * <pre>
42   * new URIBuilder("http://www.google.com/")
43   *   .setScheme( "https" )
44   *   .setPort( 443 )
45   *   .setPath( "some/path" )
46   *   .toString();
47   * </pre>
48   * A slightly more 'Groovy' version would be:
49   * <pre>
50   * new URIBuilder('http://www.google.com/').with {
51   *    scheme = 'https'
52   *    port = 443
53   *    path = 'some/path'
54   *    query = [p1:1, p2:'two']
55   * }.toString()
56   * </pre>
57   * @author <a href='mailto:tnichols@enernoc.com'>Tom Nichols</a>
58   */
59  public class URIBuilder {
60  	protected URI base;
61  	private final String ENC = "UTF-8"; 
62  	
63  	public URIBuilder( String url ) throws URISyntaxException {
64  		base = new URI(url);
65  	}
66  	
67  	public URIBuilder( URL url ) throws URISyntaxException {
68  		this.base = url.toURI();
69  	}
70  	
71  	public URIBuilder( URI url ) {
72  		this.base = url;
73  	}
74  	
75  	/**
76  	 * Attempts to convert a URL or String to a URI.
77  	 * @param uri a {@link URI}, {@link URL} or any object that produces a 
78  	 *   parse-able URI string from its <code>toString()</code> result.
79  	 * @return a valid URI parsed from the given object
80  	 * @throws URISyntaxException
81  	 */
82  	public static URI convertToURI( Object uri ) throws URISyntaxException {
83  		if ( uri instanceof URI ) ;
84  		else if ( uri instanceof URL ) uri = ((URL)uri).toURI();
85  		else uri = new URI( uri.toString() ); // assume any other object type produces a valid URI string
86  		return (URI)uri;
87  	}
88  	
89  	/**
90  	 * AKA protocol 
91  	 * @throws URISyntaxException 
92  	 */
93  	public URIBuilder setScheme( String scheme ) throws URISyntaxException {
94  		this.base = new URI( scheme, base.getUserInfo(), 
95  				base.getHost(), base.getPort(), base.getPath(),
96  				base.getQuery(), base.getFragment() );
97  		return this;
98  	}
99  	
100 	public URIBuilder setPort( int port ) throws URISyntaxException {
101 		this.base = new URI( base.getScheme(), base.getUserInfo(), 
102 				base.getHost(), port, base.getPath(),
103 				base.getQuery(), base.getFragment() );
104 		return this;
105 	}
106 	
107 	public URIBuilder setHost( String host ) throws URISyntaxException {
108 		this.base = new URI( base.getScheme(), base.getUserInfo(), 
109 				host, base.getPort(), base.getPath(),
110 				base.getQuery(), base.getFragment() );
111 		return this;
112 	}
113 	
114 	public URIBuilder setPath( String path ) throws URISyntaxException {
115 		path = base.resolve( path ).getPath();
116 		this.base = new URI( base.getScheme(), base.getUserInfo(), 
117 				base.getHost(), base.getPort(), path,
118 				base.getQuery(), base.getFragment() );
119 		return this;
120 	}
121 	
122 	/**
123 	 * Set the query portion of the URI
124 	 * @param params a Map of parameters that will be transformed into the query string
125 	 * @return
126 	 * @throws URISyntaxException
127 	 */
128 	public URIBuilder setQuery( Map<String,?> params ) throws URISyntaxException {
129 		List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());
130 		for ( Map.Entry<String, ?> entry : params.entrySet() ) {
131 			String val = ( entry.getValue() != null ) ? 
132 					entry.getValue().toString() : ""; 
133 			pairs.add( new BasicNameValuePair( 
134 					entry.getKey(), val ) );
135 		}
136 		String queryString = URLEncodedUtils.format( pairs, ENC );
137 		this.base = new URI( base.getScheme(), base.getUserInfo(), 
138 				base.getHost(), base.getPort(), base.getPath(),
139 				queryString, base.getFragment() );
140 		return this;
141 	}
142 	
143 	/**
144 	 * Get the query string as a map
145 	 * @return
146 	 */
147 	public Map<String,String> getQuery() {
148 		Map<String,String> params = new HashMap<String, String>();		
149 		List<NameValuePair> pairs = URLEncodedUtils.parse( this.base, ENC );
150 		for ( NameValuePair pair : pairs ) 
151 			params.put( pair.getName(), pair.getValue() );
152 		return params;
153 	}
154 	
155 	public boolean hasQueryParam( String name ) {
156 		return getQuery().get( name ) != null;
157 	}
158 	
159 	public URIBuilder removeQueryParam( String param ) throws URISyntaxException {
160 		Map<String,String> params = getQuery();
161 		params.remove( param );
162 		this.setQuery( params );
163 		return this;
164 	}
165 	
166 	/**
167 	 * This will append a param to the existing query string.  If the given 
168 	 * param is already part of the query string, it will be replaced.
169 	 * @param param
170 	 * @param value
171 	 * @throws URISyntaxException 
172 	 */
173 	public URIBuilder addQueryParam( String param, Object value ) throws URISyntaxException {
174 		Map<String,String> params = getQuery();
175 		if ( value == null ) value = ""; 
176 		params.put( param, value.toString() );
177 		this.setQuery( params );
178 		return this;
179 	}
180 	
181 	@SuppressWarnings("unchecked")
182 	public URIBuilder addQueryParams( Map<String,?> params ) throws URISyntaxException {
183 		Map existing = this.getQuery();
184 		existing.putAll( params );
185 		this.setQuery( existing );
186 		return this;
187 	}
188 	
189 	/**
190 	 * The document fragment, without a preceeding '#'
191 	 * @param fragment
192 	 * @throws URISyntaxException
193 	 */
194 	public URIBuilder setFragment( String fragment ) throws URISyntaxException {
195 		this.base = new URI( base.getScheme(), base.getUserInfo(), 
196 				base.getHost(), base.getPort(), base.getPath(),
197 				base.getQuery(), fragment );
198 		return this;
199 	}
200 	
201 	@Override public String toString() {
202 		return base.toString();
203 	}
204 	
205 	public URL toURL() throws MalformedURLException {
206 		return base.toURL();
207 	}
208 	
209 	public URI toURI() { return this.base; }
210 }