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

org.dominokit.rest.shared.request.ServicePath Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2019 Dominokit
 *
 * 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.dominokit.rest.shared.request;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class ServicePath {

  private static final String QUERY_REGEX = "\\?";
  private static final String FRAGMENT_REGEX = "\\#";
  private final String rootPath;
  private List paths = new LinkedList<>();
  private List queryParameters = new LinkedList<>();
  private List fragments = new LinkedList<>();

  /** @param token String, a URL token */
  public ServicePath(String token) {
    this("", token);
  }

  /**
   * @param rootPath String, the root path token
   * @param token String, a URL token
   */
  public ServicePath(String rootPath, String token) {
    if (isNull(token)) throw new IllegalArgumentException();
    this.rootPath = isNull(rootPath) ? "" : rootPath.trim();
    String rebasedToken = rebaseToken(rootPath, token);
    this.paths.addAll(asPathsList(rebasedToken));
    this.queryParameters.addAll(asQueryParameters(rebasedToken));
    this.fragments.addAll(parseFragments(rebasedToken));
  }

  private String rebaseToken(String rootPath, String token) {
    if (isNull(rootPath) || rootPath.trim().isEmpty() || !token.startsWith(rootPath)) {
      return token;
    }
    return token.substring(rootPath.length());
  }

  private List parseFragments(String token) {
    if (token.contains("#") && token.indexOf("#") < token.length() - 1)
      return asPathsList(token.split(FRAGMENT_REGEX)[1]);
    return new LinkedList<>();
  }

  /**
   * @param path The path to check for
   * @return true if the path part of the url ends with the specified path otherwise returns
   *     false
   */
  public boolean endsWithPath(String path) {
    if (isEmpty(path)) return false;
    return endsWith(paths(), asPathsList(path));
  }

  private boolean endsWith(List paths, List targets) {
    if (isValidSize(paths, targets)) return matchEnds(paths, targets);
    return false;
  }

  private boolean matchEnds(List paths, List targets) {
    int offset = paths.size() - targets.size();
    return IntStream.range(0, targets.size())
        .allMatch(i -> targets.get(i).equals(paths.get(i + offset)));
  }

  /**
   * @return a list of Strings representing all paths of a url, e.g
   *     http://localhost:8080/a/b/c will return a list contains a, b,
   *     c,
   */
  public List paths() {
    return paths;
  }

  /**
   * @return a list of Strings representing all fragments of a url, e.g
   *     http://localhost:8080/a/b/c#d/e/f will return a list contains d, d,
   *     f,
   */
  public List fragments() {
    return fragments;
  }

  /**
   * @return the path part of a url, e.g http://localhost:8080/a/b/c will return a/b/c
   */
  public String path() {
    return String.join("/", paths());
  }

  /** @return the string representing the whole query part of a token */
  public String query() {
    return queryParameters.stream().map(Parameter::asQueryString).collect(Collectors.joining("&"));
  }

  /**
   * @param name name of the query parameter
   * @return True if the token has a query param that has the specified name, otherwise
   *     returns false.
   */
  public boolean hasQueryParameter(String name) {
    Optional param =
        queryParameters.stream().filter(parameter -> parameter.key.equals(name)).findFirst();

    if (param.isPresent()) {
      return true;
    } else {
      return false;
    }
  }

  /**
   * Adds a query parameter with specified name and value to the current token, if a query parameter
   * with same name already exists, then replaces its value with the new one
   *
   * @param name query parameter name
   * @param value query parameter value
   * @return {@link ServicePath} that contains the new query parameter.
   */
  public ServicePath setQueryParameter(String name, String value) {
    if (hasQueryParameter(name)) {
      removeParameter(name);
    }
    appendParameter(name, value);
    return this;
  }

  /** {@inheritDoc} */
  public ServicePath setQueryParameter(String name, List values) {
    if (hasQueryParameter(name)) {
      removeParameter(name);
    }
    appendParameter(name, values);
    return this;
  }

  private Parameter getParameter(String name) {
    Optional param =
        queryParameters.stream().filter(parameter -> parameter.key.equals(name)).findFirst();

    if (param.isPresent()) {
      return param.get();
    } else {
      return null;
    }
  }

  /**
   * Appends a new query parameter to the end of the token query parameters part.
   *
   * @param name of the query parameter
   * @param value of the query parameter
   * @return {@link ServicePath} with the new query parameter appended to the end of query part.
   */
  public ServicePath appendParameter(String name, String value) {
    return appendParameter(name, asList(value));
  }

  /** {@inheritDoc} */
  public ServicePath appendParameter(String name, List values) {
    if (nonNull(name) && !name.trim().isEmpty()) {
      if (hasQueryParameter(name)) {
        getParameter(name).addValues(values);
      } else {
        this.queryParameters.add(new Parameter(name, values));
      }
    }
    return this;
  }

  /**
   * Replaces the first occurrence of a path segment with the replacement
   *
   * @param path The path segment to be replaced
   * @param replacement the new path segment
   * @return {@link ServicePath} with path segment replaced by the replacement
   */
  public ServicePath replacePath(String path, String replacement) {
    List paths = asPathsList(path());
    if (paths.contains(path)) {
      int i = paths.indexOf(path);
      paths.add(i, replacement);
      paths.remove(i + 1);
      this.paths = paths;
    }
    return this;
  }

  private List asList(String value) {
    List values = new ArrayList<>();
    values.add(value);
    return values;
  }

  /**
   * Removes the query parameter with the specified name
   *
   * @param name of the parameter to be removed
   * @return {@link ServicePath} with the query parameter with the specified name being removed
   */
  public ServicePath removeParameter(String name) {
    Parameter parameter = getParameter(name);
    if (nonNull(parameter)) {
      this.queryParameters.remove(parameter);
    }
    return this;
  }

  /** @return the string representing the whole fragment part of a token */
  public String fragment() {
    return String.join("/", fragments());
  }

  /**
   * @return true if all of token (path part, query part, fragments part) are empty,
   *     otherwise return false.
   */
  public boolean isEmpty() {
    return paths.isEmpty() && queryParameters.isEmpty() && fragments.isEmpty();
  }

  /** @return the full string representation of a {@link ServicePath} */
  public String value() {
    String path = path();
    String separator =
        (getRootPath().isEmpty()
                || getRootPath().endsWith("/")
                || path.startsWith("/")
                || path.isEmpty())
            ? ""
            : "/";
    return getRootPath() + separator + noRootValue();
  }

  /** {@inheritDoc} */
  public String noRootValue() {
    return path() + appendQuery(query()) + appendFragment();
  }

  private String appendFragment() {
    return isEmpty(fragment()) ? "" : "#" + fragment();
  }

  private String appendQuery(String query) {
    return isEmpty(query) ? "" : "?" + query;
  }

  private List asPathsList(String token) {
    if (isNull(token) || isEmpty(token) || token.startsWith("?") || token.startsWith("#"))
      return new ArrayList<>();
    return Arrays.stream(splittedPaths(token))
        .filter(p -> !p.isEmpty())
        .collect(Collectors.toCollection(LinkedList::new));
  }

  private String[] splittedPaths(String pathString) {
    return parsePathPart(pathString).split("/");
  }

  private String parsePathPart(String pathString) {
    return pathString.replace("!", "").split(QUERY_REGEX)[0].split(FRAGMENT_REGEX)[0];
  }

  private boolean isEmpty(String path) {
    return isNull(path) || path.isEmpty();
  }

  private boolean isValidSize(List paths, List targets) {
    return !targets.isEmpty() && targets.size() <= paths.size();
  }

  private List asQueryParameters(String token) {

    String queryString = queryPart(token);
    if (isNull(queryString) || queryString.trim().isEmpty()) {
      return new LinkedList<>();
    }
    return parsedParameters(queryString);
  }

  private List parsedParameters(String queryString) {

    return Stream.of(queryString.split("&")).map(part -> part.split("="))
        .collect(
            Collectors.groupingBy(
                keyValue -> keyValue[0],
                LinkedHashMap::new,
                Collectors.mapping(keyValue -> keyValue[1], Collectors.toList())))
        .entrySet().stream()
        .map(entry -> new Parameter(entry.getKey(), entry.getValue()))
        .collect(Collectors.toCollection(LinkedList::new));
  }

  private String queryPart(String token) {
    String query = "";
    if (token.contains("?") && token.indexOf("?") < token.length() - 1) {
      String[] parts = token.split(QUERY_REGEX);

      if (parts.length > 1) {
        if (parts[1].split(FRAGMENT_REGEX).length > 0) {
          query = parts[1].split(FRAGMENT_REGEX)[0];
        } else {
          return query;
        }
      } else {
        query = parts[0].split(FRAGMENT_REGEX)[0];
      }

      if (!query.isEmpty() && !query.contains("=")) {
        throw new IllegalArgumentException("Query string [" + query + "] is missing '=' operator.");
      }
    }
    return query;
  }

  public String getRootPath() {
    return rootPath;
  }

  public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof ServicePath)) return false;
    ServicePath that = (ServicePath) o;

    return paths.equals(that.paths)
        && fragments.equals(that.fragments)
        && queryParameters.size() == that.queryParameters.size()
        && queryParameters.containsAll(that.queryParameters);
  }

  public int hashCode() {
    return Objects.hash(paths, queryParameters, fragments);
  }

  private static class Parameter {
    private String key;
    private List value;

    public Parameter(String key, List value) {
      this.key = key;
      this.value = value;
    }

    private void addValues(List moreValues) {
      value.addAll(moreValues);
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (!(o instanceof Parameter)) return false;
      Parameter parameter = (Parameter) o;
      return Objects.equals(key, parameter.key)
          && value.size() == parameter.value.size()
          && value.containsAll(parameter.value);
    }

    @Override
    public int hashCode() {
      return Objects.hash(key, value);
    }

    private String asQueryString() {
      return value.stream().map(value -> key + "=" + value).collect(Collectors.joining("&"));
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy