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 }