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

org.codehaus.httpcache4j.uri.URIBuilder Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright (c) 2009. The Codehaus. All Rights Reserved.
 *
 *   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.
 */

package org.codehaus.httpcache4j.uri;

import net.hamnaberg.funclite.*;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Immutable URI builder.
 * Paths in this URI builder will be UTF-8 {@link org.codehaus.httpcache4j.uri.URIEncoder URIEncoded}.
 *
 * All methods return a NEW instance of the URI builder, meaning you can create a ROOT uri builder and use it
 * to your heart's content, as the instance will never change.
 *
 * @author Erlend Hamnaberg
 */
public final class URIBuilder {
    public static AtomicReference schemeDefaults = new AtomicReference(new URISchemeDefaults());

    private final String scheme;
    private final String host;
    private final int port;
    private final List path;
    private final String fragment;
    private final QueryParams parameters;
    private final boolean wasPathAbsolute;
    private final boolean endsWithSlash;
    private final String schemeSpecificPart;

    private URIBuilder(String scheme, String schemeSpecificPart, String host, int port, List path, String fragment, QueryParams parameters, boolean wasPathAbsolute, boolean endsWithSlash) {
        this.scheme = scheme;
        this.schemeSpecificPart = schemeSpecificPart;
        this.host = host;
        this.port = port;
        this.path = path;
        this.fragment = fragment;
        this.parameters = parameters;
        this.wasPathAbsolute = wasPathAbsolute;
        this.endsWithSlash = endsWithSlash;
    }

    public URIBuilder withHost(String host) {
        return new URIBuilder(scheme, schemeSpecificPart, host, port, path, fragment, parameters, wasPathAbsolute, endsWithSlash);
    }

    /**
     * Sets the port. This is not required to set if you are using default ports for 'http' or 'https'
     * @param port the port to set
     * @return a new URIBuilder with the port set
     */
    public URIBuilder withPort(int port) {
        Optional defaultPort = schemeDefaults.get().getPort(scheme);
        if (defaultPort.isSome() && (port == defaultPort.get())) {
            port = -1;
        }
        return new URIBuilder(scheme, schemeSpecificPart, host, port, path, fragment, parameters, wasPathAbsolute, endsWithSlash);
    }

    private boolean isURN() {
        return scheme != null && scheme.startsWith("urn");
    }


    /**
     * This is the scheme to use. Usually 'http' or 'https'.
     * @param scheme the scheme
     * @return a new URIBuilder with the new scheme set.
     */
    public URIBuilder withScheme(String scheme) {
        return new URIBuilder(scheme, schemeSpecificPart, host, port, path, fragment, parameters, wasPathAbsolute, endsWithSlash);
    }

    /**
     * Adds a raw path to the URI.
     * @param path a path which may contain '/'
     * @return a new URI builder which contains the added path.
     */
    public URIBuilder addRawPath(String path) {
        boolean pathAbsolute = wasPathAbsolute || this.path.isEmpty() && path.startsWith("/");
        boolean endsWithSlash = this.endsWithSlash || this.path.isEmpty() && path.endsWith("/");
        List appendedPath = toPathParts(path);
        ArrayList currentPath = new ArrayList();
        currentPath.addAll(this.path);
        currentPath.addAll(appendedPath);
        return pathInternal(currentPath, pathAbsolute, endsWithSlash);
    }

    /**
     * Appends the path part to the URI.
     * We do not expect the path separator '/' to appear here, as each element will be URLEncoded.
     * If the '/' character do appear it will be URLEncoded with the rest of the path.
     *
     * @param path path elements.
     * @return a new URI builder which contains the new path.
     */
    public URIBuilder addPath(List path) {
        List appendedPath = CollectionOps.map(path, stringToPath);
        ArrayList currentPath = new ArrayList();
        currentPath.addAll(this.path);
        currentPath.addAll(appendedPath);
        return pathInternal(currentPath, wasPathAbsolute, false);
    }

    /**
     * @see #addPath(java.util.List)
     *
     * @param path path elements
     * @return a new URI builder which contains the new path.
     */
    public URIBuilder addPath(String... path) {
        return addPath(Arrays.asList(path));
    }

    /**
     * @see #withPath(java.util.List)
     *
     * @param path path elements.
     * @return a new URI builder which contains the new path.
     */
    public URIBuilder withPath(String... path) {
        return withPath(Arrays.asList(path));
    }

    /**
     * Sets the path of the uri.
     * We do not expect the path separator '/' to appear here, as each element will be URLEncoded.
     * If the '/' character do appear it will be URLEncoded with the rest of the path.
     *
     * @param pathList path elements.
     * @return a new URI builder which contains the new path.
     */
    public URIBuilder withPath(List pathList) {
        List paths = CollectionOps.map(pathList, stringToPath);
        return pathInternal(paths, false, false);
    }

    /**
     * @see #withPath(java.util.List)
     *
     * @param path path elements.
     * @return a new URI builder which contains the new path.
     */
    public URIBuilder withRawPath(String path) {
        boolean pathAbsoulute = path.startsWith("/");
        boolean endsWithSlash = path.endsWith("/");
        List parts = toPathParts(path);
        return pathInternal(parts, pathAbsoulute, endsWithSlash);
    }

    private URIBuilder pathInternal(List pathList, boolean pathAbsolute, boolean endsWithSlash) {
        return new URIBuilder(scheme, schemeSpecificPart, host, port, pathList, fragment, parameters, pathAbsolute, endsWithSlash);
    }

    public URIBuilder withFragment(String fragment) {
        return new URIBuilder(scheme, schemeSpecificPart, host, port, path, fragment, parameters, wasPathAbsolute, endsWithSlash);
    }

    /**
     * Creates a new URIBuilder with no parameters, but all other values retained.
     * @return new URIBuilder with no parameters.
     */
    public URIBuilder noParameters() {
        return withParameters(parameters.empty());
    }

    /**
     * Sets a list of parameters. This will clear out all previously set parameters in the new instance.
     * @param parameters the list of parameters
     * @return new URIBuilder with parameters.
     */
    public URIBuilder withParameters(Iterable parameters) {
        QueryParams updated = this.parameters.set(parameters);
        return withParameters(updated);
    }

    public URIBuilder withParameters(Map> params) {
        QueryParams updated = this.parameters.set(params);
        return withParameters(updated);
    }

    public URIBuilder withParameters(QueryParams params) {
        return new URIBuilder(scheme, schemeSpecificPart, host, port, path, fragment, params, wasPathAbsolute, endsWithSlash);
    }


    /**
     * Adds a new Parameter to the collection of parameters
     * @param name the parameter name
     * @param value the parameter value
     * @return a new instance of the URIBuilder
     */
    public URIBuilder addParameter(String name, String value) {
        return addParameter(new QueryParam(name, value));
    }

    /**
     * Adds a new Parameter to the collection of parameters
     * @param parameter the parameter
     * @return a new instance of the URIBuilder
     */
    public URIBuilder addParameter(QueryParam parameter) {
        return addParameters(Arrays.asList(parameter));
    }

    /**
     * Adds Parameters to the collection of parameters
     * @return a new instance of the URIBuilder
     */
    public URIBuilder addParameters(Iterable newParams) {
        if (!newParams.iterator().hasNext()) {
            return this;
        }
        QueryParams updated = this.parameters.add(newParams);
        return withParameters(updated);
    }

    public URIBuilder addParameters(String name, String... values) {
        QueryParams updated = this.parameters.add(name, values);
        return withParameters(updated);
    }

    public URIBuilder addParameters(Map> newParams) {
        if (newParams.isEmpty()) {
            return this;
        }
        QueryParams updated = this.parameters.add(newParams);
        return withParameters(updated);
    }

    public URIBuilder removeParameters(String name) {
        return withParameters(this.parameters.remove(name));
    }

    public URIBuilder replaceParameter(String name, String value) {
        return withParameters(this.parameters.set(name, value));
    }

    private String toPath(boolean encodepath) {
        if (path.isEmpty()) {
            return null;
        }
        StringBuilder builder = new StringBuilder();
        for (Path pathElement : path) {
            if (builder.length() > 0) {
                builder.append("/");
            }
            builder.append(encodepath ? pathElement.getEncodedValue() : pathElement.getValue());
        }
        if ((wasPathAbsolute || host != null) && builder.length() > 1) {
            if (!"/".equals(builder.substring(0, 1))) {
                builder.insert(0, "/");                
            }
        }
        if (endsWithSlash) {
            builder.append("/");
        }
        return builder.toString();
    }

    public URI toURI() {
        return toURI(true, false, false);
    }

    public URI toNormalizedURI(boolean encodePath) {
        return toURI(encodePath, true, false).normalize();
    }

    public URI toNormalizedURI() {
        return toNormalizedURI(true);
    }

    /**
     * @return true if the scheme and host parts are not set.
     */
    public boolean isRelative() {
        return (scheme == null && host == null);
    }

    public URI toAbsoluteURI() {
        return toURI(true, false, true);
    }

    private URI toURI(boolean encodePath, boolean sortQP, boolean absolutify) {
        try {
            if (isURN()) {
                return new URI(scheme, schemeSpecificPart, fragment);
            }
            StringBuilder sb = new StringBuilder();
            if (scheme != null) {
                sb.append(scheme);
                sb.append("://");
            }
            if (host != null) {
                sb.append(host);
            }
            if (port > -1) {
                sb.append(":").append(port);
            }
            if (!path.isEmpty()) {
                String path = toPath(encodePath);
                if (absolutify && isRelative() && !path.startsWith("/")) {
                    path = "/" + path;
                }
                sb.append(path);
            }
            if (!parameters.isEmpty()) {
                sb.append("?");
                sb.append(parameters.toQuery(sortQP));
            }
            if (fragment != null) {
                sb.append("#");
                sb.append(fragment);
            }
            return URI.create(sb.toString());
        } catch (URISyntaxException e) {
            throw new IllegalStateException(e);
        }
    }

    
    public List getParametersByName(final String name) {
        List params = parameters.get(name);
        if (params == null) {
            return Collections.emptyList();
        }
        return CollectionOps.map(params, new Function() {
            public QueryParam apply(String s) {
                return new QueryParam(name, s);
            }
        });
    }

    public String getFirstParameterValueByName(final String name) {
        return parameters.getFirst(name);
    }

    /**
     * Constructs a new URIBuilder from the given URI
     * @param uri the uri to use
     * @return a new URIBuilder which has the information from the URI.
     */
    public static URIBuilder fromURI(URI uri) {
        boolean pathAbsoluteness = uri.getPath() != null && uri.getPath().startsWith("/");
        boolean endsWithSlash = uri.getPath() != null && uri.getPath().endsWith("/");
        return new URIBuilder(uri.getScheme(), uri.getSchemeSpecificPart(), uri.getHost(), uri.getPort(), toPathParts(uri.getPath()), uri.getFragment(), QueryParams.parse(uri.getRawQuery()), pathAbsoluteness, endsWithSlash);
    }

    /**
     * Creates an empty URIBuilder.
     * @return an empty URIBuilder which result of {@link #toURI()} ()} will return "".
     */
    public static URIBuilder empty() {
        return new URIBuilder(null, "", null, -1, Collections.emptyList(), null, new QueryParams(), false, false);
    }

    public String getScheme() {
        return scheme;
    }

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }

    public List getPath() {
        return CollectionOps.map(path, pathToString);
    }

    public List getEncodedPath() {
        return CollectionOps.map(path, encodedPathToString);
    }

    public String getCurrentPath() {
        return toPath(false);
    }

    public String getFragment() {
        return fragment;
    }

    public QueryParams getParameters() {
        return parameters;
    }

    public Map> getParametersAsMap() {
        return parameters.asMap();
    }

    private static List toPathParts(String path) {
        if (path == null) {
            return Collections.emptyList();
        }
        if (!path.contains("/")) {
            return Collections.singletonList(new Path(path));
        }
        else {
            if (path.startsWith("/")) {
                path = path.substring(1);
            }
            List stringList = Arrays.asList(path.split("/"));
            return CollectionOps.map(stringList, stringToPath);
        }
    }

    private static Function stringToPath = new Function() {
        public Path apply(String from) {
            return new Path(from);
        }
    };

    private static Function pathToString = new Function() {
        public String apply(Path from) {
            return from.getValue();
        }
    };

    private static Function encodedPathToString = new Function() {
        public String apply(Path from) {
            return from.getEncodedValue();
        }
    };

    private static class Path {
        private final String value;

        private Path(String value) {
            this.value = URIDecoder.decodeUTF8(value);
        }

        String getEncodedValue() {
            return URIEncoder.encodeUTF8(value);
        }

        String getValue() {
            return value;
        }

        @Override
        public String toString() {
            return value;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy