001 /*
002 * Copyright 2003-2008 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * You are receiving this code free of charge, which represents many hours of
017 * effort from other individuals and corporations. As a responsible member
018 * of the community, you are asked (but not required) to donate any
019 * enhancements or improvements back to the community under a similar open
020 * source license. Thank you. -TMN
021 */
022 package groovyx.net.http;
023
024 import java.net.MalformedURLException;
025 import java.net.URI;
026 import java.net.URISyntaxException;
027 import java.net.URL;
028 import java.util.ArrayList;
029 import java.util.HashMap;
030 import java.util.List;
031 import java.util.Map;
032
033 import org.apache.http.NameValuePair;
034 import org.apache.http.client.utils.URLEncodedUtils;
035 import org.apache.http.message.BasicNameValuePair;
036
037 /**
038 * This class implements a mutable URI. All <code>set</code>, <code>add</code>
039 * and <code>remove</code> methods affect this class' internal URI
040 * representation. All mutator methods support chaining, e.g.
041 * <pre>
042 * new URIBuilder("http://www.google.com/")
043 * .setScheme( "https" )
044 * .setPort( 443 )
045 * .setPath( "some/path" )
046 * .toString();
047 * </pre>
048 * A slightly more 'Groovy' version would be:
049 * <pre>
050 * new URIBuilder('http://www.google.com/').with {
051 * scheme = 'https'
052 * port = 443
053 * path = 'some/path'
054 * query = [p1:1, p2:'two']
055 * }.toString()
056 * </pre>
057 * @author <a href='mailto:tnichols@enernoc.com'>Tom Nichols</a>
058 */
059 public class URIBuilder {
060 protected URI base;
061 private final String ENC = "UTF-8";
062
063 public URIBuilder( String url ) throws URISyntaxException {
064 base = new URI(url);
065 }
066
067 public URIBuilder( URL url ) throws URISyntaxException {
068 this.base = url.toURI();
069 }
070
071 public URIBuilder( URI url ) {
072 this.base = url;
073 }
074
075 /**
076 * Attempts to convert a URL or String to a URI.
077 * @param uri a {@link URI}, {@link URL} or any object that produces a
078 * parse-able URI string from its <code>toString()</code> result.
079 * @return a valid URI parsed from the given object
080 * @throws URISyntaxException
081 */
082 public static URI convertToURI( Object uri ) throws URISyntaxException {
083 if ( uri instanceof URI ) ;
084 else if ( uri instanceof URL ) uri = ((URL)uri).toURI();
085 else uri = new URI( uri.toString() ); // assume any other object type produces a valid URI string
086 return (URI)uri;
087 }
088
089 /**
090 * AKA protocol
091 * @throws URISyntaxException
092 */
093 public URIBuilder setScheme( String scheme ) throws URISyntaxException {
094 this.base = new URI( scheme, base.getUserInfo(),
095 base.getHost(), base.getPort(), base.getPath(),
096 base.getQuery(), base.getFragment() );
097 return this;
098 }
099
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 }