All Downloads are FREE. Search and download functionalities are using the official Maven repository.

groovyx.net.http.URIBuilder Maven / Gradle / Ivy

Go to download

A builder-style HTTP client API, including authentication, and extensible handling of common content-types such as JSON and XML. It is built on top of Apache's HttpClient.

There is a newer version: 0.7.1
Show newest version
/*
 * Copyright 2008-2011 Thomas Nichols.  http://blog.thomnichols.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * You are receiving this code free of charge, which represents many hours of
 * effort from other individuals and corporations.  As a responsible member
 * of the community, you are encouraged (but not required) to donate any
 * enhancements or improvements back to the community under a similar open
 * source license.  Thank you. -TMN
 */
package groovyx.net.http;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;

/**
 * This class implements a mutable URI.  All set, add
 * and remove methods affect this class' internal URI
 * representation.  All mutator methods support chaining, e.g.
 * 
 * new URIBuilder("http://www.google.com/")
 *   .setScheme( "https" )
 *   .setPort( 443 )
 *   .setPath( "some/path" )
 *   .toString();
 * 
* A slightly more 'Groovy' version would be: *
 * new URIBuilder('http://www.google.com/').with {
 *    scheme = 'https'
 *    port = 443
 *    path = 'some/path'
 *    query = [p1:1, p2:'two']
 * }.toString()
 * 
* @author Tom Nichols */ public class URIBuilder implements Cloneable { protected URI base; private final String ENC = "UTF-8"; public URIBuilder( String url ) throws URISyntaxException { base = new URI(url); } public URIBuilder( URL url ) throws URISyntaxException { this.base = url.toURI(); } /** * @throws IllegalArgumentException if uri is null * @param uri */ public URIBuilder( URI uri ) throws IllegalArgumentException { if ( uri == null ) throw new IllegalArgumentException( "uri cannot be null" ); this.base = uri; } /** * Utility method to convert a number of type to a URI instance. * @param uri a {@link URI}, {@link URL} or any object that produces a * valid URI string from its toString() result. * @return a valid URI parsed from the given object * @throws URISyntaxException */ public static URI convertToURI( Object uri ) throws URISyntaxException { if ( uri instanceof URI ) return (URI)uri; if ( uri instanceof URL ) return ((URL)uri).toURI(); if ( uri instanceof URIBuilder ) return ((URIBuilder)uri).toURI(); return new URI( uri.toString() ); // assume any other object type produces a valid URI string } protected URI update( String scheme, String userInfo, String host, int port, String path, String query, String fragment ) throws URISyntaxException { URI u = new URI( scheme, userInfo, host, port, base.getPath(), null, null ); StringBuilder sb = new StringBuilder(); if ( path != null ) sb.append( path ); if ( query != null ) sb.append( '?' ).append( query ); if ( fragment != null ) sb.append( '#' ).append( fragment ); return u.resolve( sb.toString() ); } /** * Set the URI scheme, AKA the 'protocol.' e.g. * setScheme('https') * @throws URISyntaxException if the given scheme contains illegal characters. */ public URIBuilder setScheme( String scheme ) throws URISyntaxException { this.base = update( scheme, base.getUserInfo(), base.getHost(), base.getPort(), base.getRawPath(), base.getRawQuery(), base.getRawFragment() ); return this; } /** * Get the scheme for this URI. See {@link URI#getScheme()} * @return the scheme portion of the URI */ public String getScheme() { return this.base.getScheme(); } /** * Set the port for this URI, or -1 to unset the port. * @param port * @return this URIBuilder instance * @throws URISyntaxException */ public URIBuilder setPort( int port ) throws URISyntaxException { this.base = update( base.getScheme(), base.getUserInfo(), base.getHost(), port, base.getRawPath(), base.getRawQuery(), base.getRawFragment() ); return this; } /** * See {@link URI#getPort()} * @return the port portion of this URI (-1 if a port is not specified.) */ public int getPort() { return this.base.getPort(); } /** * Set the host portion of this URI. * @param host * @return this URIBuilder instance * @throws URISyntaxException if the host parameter contains illegal characters. */ public URIBuilder setHost( String host ) throws URISyntaxException { this.base = update( base.getScheme(), base.getUserInfo(), host, base.getPort(), base.getRawPath(), base.getRawQuery(), base.getRawFragment() ); return this; } /** * See {@link URI#getHost()} * @return the host portion of the URI */ public String getHost() { return base.getHost(); } /** * Set the path component of this URI. The value may be absolute or * relative to the current path. * e.g.
     *   def uri = new URIBuilder( 'http://localhost/p1/p2?a=1' )
     *
     *   uri.path = '/p3/p2'
     *   assert uri.toString() == 'http://localhost/p3/p2?a=1'
     *
     *   uri.path = 'p2a'
     *   assert uri.toString() == 'http://localhost/p3/p2a?a=1'
     *
     *   uri.path = '../p4'
     *   assert uri.toString() == 'http://localhost/p4?a=1&b=2&c=3#frag'
     * 
     * @param path the path portion of this URI, relative to the current URI.
     * @return this URIBuilder instance, for method chaining.
     * @throws URISyntaxException if the given path contains characters that
     *   cannot be converted to a valid URI
     */
    public URIBuilder setPath( String path ) throws URISyntaxException {
        this.base = update( base.getScheme(), base.getUserInfo(),
                base.getHost(), base.getPort(),
                new URI( null, null, path, null, null ).getRawPath(),
                base.getRawQuery(), base.getRawFragment() );
        return this;
    }

    /**
     * Note that this property is not necessarily reflexive
     * with the {@link #setPath(String)} method!  URIBuilder.setPath()
     * will resolve a relative path, whereas this method will always return the
     * full, absolute path.
     * See {@link URI#getPath()}
     * @return the full path portion of the URI.
     */
    public String getPath() {
        return this.base.getPath();
    }

    /* TODO null/ zero-size check if this is ever made public */
    protected URIBuilder setQueryNVP( List nvp ) throws URISyntaxException {
        /* Passing the query string in the URI constructor will
         * double-escape query parameters and goober things up.  So we have
         * to create a full path+query+fragment and use URI#resolve() to
         * create the new URI.  */
        StringBuilder sb = new StringBuilder();
        String path = base.getRawPath();
        if ( path != null ) sb.append( path );
        sb.append( '?' );
        sb.append( URLEncodedUtils.format( nvp, ENC ) );
        String frag = base.getRawFragment();
        if ( frag != null ) sb.append( '#' ).append( frag );
        this.base = base.resolve( sb.toString() );

        return this;
    }

    /**
     * Set the query portion of the URI.  For query parameters with multiple
     * values, put the values in a list like so:
     * 
uri.query = [ p1:'val1', p2:['val2', 'val3'] ]
     * // will produce a query string of ?p1=val1&p2=val2&p2=val3
* * @param params a Map of parameters that will be transformed into the query string * @return this URIBuilder instance, for method chaining. * @throws URISyntaxException */ public URIBuilder setQuery( Map params ) throws URISyntaxException { if ( params == null || params.size() < 1 ) { this.base = new URI( base.getScheme(), base.getUserInfo(), base.getHost(), base.getPort(), base.getPath(), null, base.getFragment() ); } else { List nvp = new ArrayList(params.size()); for ( Object key : params.keySet() ) { Object value = params.get(key); if ( value instanceof List ) { for (Object val : (List)value ) nvp.add( new BasicNameValuePair( key.toString(), ( val != null ) ? val.toString() : "" ) ); } else nvp.add( new BasicNameValuePair( key.toString(), ( value != null ) ? value.toString() : "" ) ); } this.setQueryNVP( nvp ); } return this; } /** * Set the raw, already-escaped query string. No additional escaping will * be done on the string. * @param query * @return */ public URIBuilder setRawQuery( String query ) throws URISyntaxException { this.base = update( base.getScheme(), base.getUserInfo(), base.getHost(), base.getPort(), base.getRawPath(), query, base.getRawFragment() ); return this; } /** * Get the query string as a map for convenience. If any parameter contains * multiple values (e.g. p1=one&p1=two) both values will be * inserted into a list for that paramter key ([p1 : ['one','two']] * ). Note that this is not a "live" map. Therefore, you cannot * call *
 uri.query.a = 'BCD'
* You will not modify the query string but instead the generated map of * parameters. Instead, you need to use {@link #removeQueryParam(String)} * first, then {@link #addQueryParam(String, Object)}, or call * {@link #setQuery(Map)} which will set the entire query string. * @return a map of String name/value pairs representing the URI's query * string. */ public Map getQuery() { Map params = new HashMap(); List pairs = this.getQueryNVP(); if ( pairs == null ) return null; for ( NameValuePair pair : pairs ) { String key = pair.getName(); Object existing = params.get( key ); if ( existing == null ) params.put( key, pair.getValue() ); else if ( existing instanceof List ) ((List)existing).add( pair.getValue() ); else { List vals = new ArrayList(2); vals.add( (String)existing ); vals.add( pair.getValue() ); params.put( key, vals ); } } return params; } protected List getQueryNVP() { if ( this.base.getQuery() == null ) return null; List nvps = URLEncodedUtils.parse( this.base, ENC ); List newList = new ArrayList(); if ( nvps != null ) newList.addAll( nvps ); return newList; } /** * Indicates if the given parameter is already part of this URI's query * string. * @param name the query parameter name * @return true if the given parameter name is found in the query string of * the URI. */ public boolean hasQueryParam( String name ) { return getQuery().get( name ) != null; } /** * Remove the given query parameter from this URI's query string. * @param param the query name to remove * @return this URIBuilder instance, for method chaining. * @throws URISyntaxException */ public URIBuilder removeQueryParam( String param ) throws URISyntaxException { List params = getQueryNVP(); NameValuePair found = null; for ( NameValuePair nvp : params ) // BOO linear search. Assume the list is small. if ( nvp.getName().equals( param ) ) { found = nvp; break; } if ( found == null ) throw new IllegalArgumentException( "Param '" + param + "' not found" ); params.remove( found ); this.setQueryNVP( params ); return this; } protected URIBuilder addQueryParam( NameValuePair nvp ) throws URISyntaxException { List params = getQueryNVP(); if ( params == null ) params = new ArrayList(); params.add( nvp ); this.setQueryNVP( params ); return this; } /** * This will append a query parameter to the existing query string. If the given * parameter is already part of the query string, it will be appended to. * To replace the existing value of a certain parameter, either call * {@link #removeQueryParam(String)} first, or use {@link #getQuery()}, * modify the value in the map, then call {@link #setQuery(Map)}. * @param param query parameter name * @param value query parameter value (will be converted to a string if * not null. If value is null, it will be set as the empty * string. * @return this URIBuilder instance, for method chaining. * @throws URISyntaxException if the query parameter values cannot be * converted to a valid URI. * @see #setQuery(Map) */ public URIBuilder addQueryParam( String param, Object value ) throws URISyntaxException { this.addQueryParam( new BasicNameValuePair( param, ( value != null ) ? value.toString() : "" ) ); return this; } protected URIBuilder addQueryParams( List nvp ) throws URISyntaxException { List params = getQueryNVP(); if ( params == null ) params = new ArrayList(); params.addAll( nvp ); this.setQueryNVP( params ); return this; } /** * Add these parameters to the URIBuilder's existing query string. * Parameters may be passed either as a single map argument, or as a list * of named arguments. e.g. *
 uriBuilder.addQueryParams( [one:1,two:2] )
     * uriBuilder.addQueryParams( three : 3 ) 
* * If any of the parameters already exist in the URI query, these values * will not replace them. Multiple values for the same * query parameter may be added by putting them in a list. See * {@link #setQuery(Map)}. * * @param params parameters to add to the existing URI query (if any). * @return this URIBuilder instance, for method chaining. * @throws URISyntaxException */ @SuppressWarnings("unchecked") public URIBuilder addQueryParams( Map params ) throws URISyntaxException { List nvp = new ArrayList(); for ( Object key : params.keySet() ) { Object value = params.get( key ); if ( value instanceof List ) { for ( Object val : (List)value ) nvp.add( new BasicNameValuePair( key.toString(), ( val != null ) ? val.toString() : "" ) ); } else nvp.add( new BasicNameValuePair( key.toString(), ( value != null ) ? value.toString() : "" ) ); } this.addQueryParams( nvp ); return this; } /** * The document fragment, without a preceeding '#'. Use null * to use no document fragment. * @param fragment * @return this URIBuilder instance, for method chaining. * @throws URISyntaxException if the given value contains illegal characters. */ public URIBuilder setFragment( String fragment ) throws URISyntaxException { this.base = update( base.getScheme(), base.getUserInfo(), base.getHost(), base.getPort(), base.getRawPath(), base.getRawQuery(), new URI( null, null, null, fragment ).getRawFragment() ); return this; } /** * See {@link URI#getFragment()} * @return the URI document fragment */ public String getFragment() { return this.base.getFragment(); } /** * Set the userInfo portion of the URI, or null if the URI * should have no user information. * @param userInfo * @return this URIBuilder instance * @throws URISyntaxException if the given value contains illegal characters. */ public URIBuilder setUserInfo( String userInfo ) throws URISyntaxException { this.base = update( base.getScheme(), userInfo, base.getHost(), base.getPort(), base.getRawPath(), base.getRawQuery(), base.getRawFragment() ); return this; } /** * See {@link URI#getUserInfo()} * @return the user info portion of the URI, or null if it * is not specified. */ public String getUserInfo() { return this.base.getUserInfo(); } /** * Print this builder's URI representation. */ @Override public String toString() { return base.toString(); } /** * Convenience method to convert this object to a URL instance. * @return this builder as a URL * @throws MalformedURLException if the underlying URI does not represent a * valid URL. */ public URL toURL() throws MalformedURLException { return base.toURL(); } /** * Convenience method to convert this object to a URI instance. * @return this builder's underlying URI representation */ public URI toURI() { return this.base; } /** * Implementation of Groovy's as operator, to allow type * conversion. * @param type URL, URL, or String. * @return a representation of this URIBuilder instance in the given type * @throws MalformedURLException if type is URL and this * URIBuilder instance does not represent a valid URL. */ public Object asType( Class type ) throws MalformedURLException { if ( type == URI.class ) return this.toURI(); if ( type == URL.class ) return this.toURL(); if ( type == String.class ) return this.toString(); throw new ClassCastException( "Cannot cast instance of URIBuilder to class " + type ); } /** * Create a copy of this URIBuilder instance. */ @Override protected URIBuilder clone() { return new URIBuilder( this.base ); } /** * Determine if this URIBuilder is equal to another URIBuilder instance. * @see URI#equals(Object) * @return if obj is a URIBuilder instance whose underlying * URI implementation is equal to this one's. */ @Override public boolean equals( Object obj ) { if ( ! ( obj instanceof URIBuilder) ) return false; return this.base.equals( ((URIBuilder)obj).toURI() ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy