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

com.wl4g.infra.common.remoting.uri.DefaultUriBuilderFactory Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 ~ 2025 the original author or authors. James Wong 
 *
 * 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 com.wl4g.infra.common.remoting.uri;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Nullable;
import com.wl4g.infra.common.collection.multimap.MultiValueMap;
import com.wl4g.infra.common.lang.ObjectUtils2;
import com.wl4g.infra.common.lang.StringUtils2;

/**
 * {@code UriBuilderFactory} that relies on {@link UriComponentsBuilder} for the
 * actual building of the URI.
 *
 * 

* Provides options to create {@link UriBuilder} instances with a common base * URI, alternative encoding mode strategies, among others. * * @see UriComponentsBuilder */ public class DefaultUriBuilderFactory implements UriBuilderFactory { @Nullable private final UriComponentsBuilder baseUri; private EncodingMode encodingMode = EncodingMode.TEMPLATE_AND_VALUES; private final Map defaultUriVariables = new HashMap<>(); private boolean parsePath = true; /** * Default constructor without a base URI. *

* The target address must be specified on each UriBuilder. */ public DefaultUriBuilderFactory() { this.baseUri = null; } public DefaultUriBuilderFactory(EncodingMode encodingMode) { this.baseUri = null; setEncodingMode(encodingMode); } /** * Constructor with a base URI. *

* The given URI template is parsed via * {@link UriComponentsBuilder#fromUriString} and then applied as a base URI * to every UriBuilder via {@link UriComponentsBuilder#uriComponents} unless * the UriBuilder itself was created with a URI template that already has a * target address. * * @param baseUriTemplate * the URI template to use a base URL */ public DefaultUriBuilderFactory(String baseUriTemplate) { this.baseUri = UriComponentsBuilder.fromUriString(baseUriTemplate); } /** * Variant of {@link #DefaultUriBuilderFactory(String)} with a * {@code UriComponentsBuilder}. */ public DefaultUriBuilderFactory(UriComponentsBuilder baseUri) { this.baseUri = baseUri; } /** * Set the encoding mode to use. * */ public void setEncodingMode(EncodingMode encodingMode) { this.encodingMode = encodingMode; } /** * Return the configured encoding mode. */ public EncodingMode getEncodingMode() { return this.encodingMode; } /** * Provide default URI variable values to use when expanding URI templates * with a Map of variables. * * @param defaultUriVariables * default URI variable values */ public void setDefaultUriVariables(@Nullable Map defaultUriVariables) { this.defaultUriVariables.clear(); if (defaultUriVariables != null) { this.defaultUriVariables.putAll(defaultUriVariables); } } /** * Return the configured default URI variable values. */ public Map getDefaultUriVariables() { return Collections.unmodifiableMap(this.defaultUriVariables); } /** * Whether to parse the input path into path segments if the encoding mode * is set to {@link EncodingMode#URI_COMPONENT EncodingMode.URI_COMPONENT}, * which ensures that URI variables in the path are encoded according to * path segment rules and for example a '/' is encoded. *

* By default this is set to {@code true}. * * @param parsePath * whether to parse the path into path segments */ public void setParsePath(boolean parsePath) { this.parsePath = parsePath; } /** * Whether to parse the path into path segments if the encoding mode is set * to {@link EncodingMode#URI_COMPONENT EncodingMode.URI_COMPONENT}. */ public boolean shouldParsePath() { return this.parsePath; } // UriTemplateHandler @Override public URI expand(String uriTemplate, Map uriVars) { return uriString(uriTemplate).build(uriVars); } @Override public URI expand(String uriTemplate, Object... uriVars) { return uriString(uriTemplate).build(uriVars); } // UriBuilderFactory @Override public UriBuilder uriString(String uriTemplate) { return new DefaultUriBuilder(uriTemplate); } @Override public UriBuilder builder() { return new DefaultUriBuilder(""); } /** * {@link DefaultUriBuilderFactory} specific implementation of UriBuilder. */ private class DefaultUriBuilder implements UriBuilder { private final UriComponentsBuilder uriComponentsBuilder; public DefaultUriBuilder(String uriTemplate) { this.uriComponentsBuilder = initUriComponentsBuilder(uriTemplate); } private UriComponentsBuilder initUriComponentsBuilder(String uriTemplate) { UriComponentsBuilder result; if (StringUtils2.isBlank(uriTemplate)) { result = (baseUri != null ? baseUri.cloneBuilder() : UriComponentsBuilder.newInstance()); } else if (baseUri != null) { UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(uriTemplate); UriComponents uri = builder.build(); result = (uri.getHost() == null ? baseUri.cloneBuilder().uriComponents(uri) : builder); } else { result = UriComponentsBuilder.fromUriString(uriTemplate); } if (encodingMode.equals(EncodingMode.TEMPLATE_AND_VALUES)) { result.encode(); } parsePathIfNecessary(result); return result; } private void parsePathIfNecessary(UriComponentsBuilder result) { if (parsePath && encodingMode.equals(EncodingMode.URI_COMPONENT)) { UriComponents uric = result.build(); String path = uric.getPath(); result.replacePath(null); for (String segment : uric.getPathSegments()) { result.pathSegment(segment); } if (path != null && path.endsWith("/")) { result.path("/"); } } } @Override public DefaultUriBuilder scheme(@Nullable String scheme) { this.uriComponentsBuilder.scheme(scheme); return this; } @Override public DefaultUriBuilder userInfo(@Nullable String userInfo) { this.uriComponentsBuilder.userInfo(userInfo); return this; } @Override public DefaultUriBuilder host(@Nullable String host) { this.uriComponentsBuilder.host(host); return this; } @Override public DefaultUriBuilder port(int port) { this.uriComponentsBuilder.port(port); return this; } @Override public DefaultUriBuilder port(@Nullable String port) { this.uriComponentsBuilder.port(port); return this; } @Override public DefaultUriBuilder path(String path) { this.uriComponentsBuilder.path(path); return this; } @Override public DefaultUriBuilder replacePath(@Nullable String path) { this.uriComponentsBuilder.replacePath(path); return this; } @Override public DefaultUriBuilder pathSegment(String... pathSegments) { this.uriComponentsBuilder.pathSegment(pathSegments); return this; } @Override public DefaultUriBuilder query(String query) { this.uriComponentsBuilder.query(query); return this; } @Override public DefaultUriBuilder replaceQuery(@Nullable String query) { this.uriComponentsBuilder.replaceQuery(query); return this; } @Override public DefaultUriBuilder queryParam(String name, Object... values) { this.uriComponentsBuilder.queryParam(name, values); return this; } @Override public DefaultUriBuilder queryParam(String name, @Nullable Collection values) { this.uriComponentsBuilder.queryParam(name, values); return this; } @Override public DefaultUriBuilder replaceQueryParam(String name, Object... values) { this.uriComponentsBuilder.replaceQueryParam(name, values); return this; } @Override public DefaultUriBuilder replaceQueryParam(String name, @Nullable Collection values) { this.uriComponentsBuilder.replaceQueryParam(name, values); return this; } @Override public DefaultUriBuilder queryParams(MultiValueMap params) { this.uriComponentsBuilder.queryParams(params); return this; } @Override public DefaultUriBuilder replaceQueryParams(MultiValueMap params) { this.uriComponentsBuilder.replaceQueryParams(params); return this; } @Override public DefaultUriBuilder fragment(@Nullable String fragment) { this.uriComponentsBuilder.fragment(fragment); return this; } @Override public URI build(Map uriVars) { if (!defaultUriVariables.isEmpty()) { Map map = new HashMap<>(); map.putAll(defaultUriVariables); map.putAll(uriVars); uriVars = map; } if (encodingMode.equals(EncodingMode.VALUES_ONLY)) { uriVars = UriUtils.encodeUriVariables(uriVars); } UriComponents uric = this.uriComponentsBuilder.build().expand(uriVars); return createUri(uric); } @Override public URI build(Object... uriVars) { if (ObjectUtils2.isEmpty(uriVars) && !defaultUriVariables.isEmpty()) { return build(Collections.emptyMap()); } if (encodingMode.equals(EncodingMode.VALUES_ONLY)) { uriVars = UriUtils.encodeUriVariables(uriVars); } UriComponents uric = this.uriComponentsBuilder.build().expand(uriVars); return createUri(uric); } private URI createUri(UriComponents uric) { if (encodingMode.equals(EncodingMode.URI_COMPONENT)) { uric = uric.encode(); } return URI.create(uric.toString()); } } /** * Enum to represent multiple URI encoding strategies. * * @see #setEncodingMode */ public enum EncodingMode { /** * Pre-encode the URI template first, then strictly encode URI variables * when expanded, with the following rules: *

    *
  • For the URI template replace only non-ASCII and illegal * (within a given URI component type) characters with escaped octets. *
  • For URI variables do the same and also replace characters with * reserved meaning. *
*

* For most cases, this mode is most likely to give the expected result * because in treats URI variables as opaque data to be fully encoded, * while {@link #URI_COMPONENT} by comparison is useful only if * intentionally expanding URI variables with reserved characters. * * @since 5.0.8 * @see UriComponentsBuilder#encode() */ TEMPLATE_AND_VALUES, /** * Does not encode the URI template and instead applies strict encoding * to URI variables via {@link UriUtils#encodeUriVariables} prior to * expanding them into the template. * * @see UriUtils#encodeUriVariables(Object...) * @see UriUtils#encodeUriVariables(Map) */ VALUES_ONLY, /** * Expand URI variables first, and then encode the resulting URI * component values, replacing only non-ASCII and illegal * (within a given URI component type) characters, but not characters * with reserved meaning. * * @see UriComponents#encode() */ URI_COMPONENT, /** * No encoding should be applied. */ NONE } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy