QueryStringBuilder.java

  1. /*
  2.  * #%L
  3.  * wcm.io
  4.  * %%
  5.  * Copyright (C) 2018 wcm.io
  6.  * %%
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *      http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  * #L%
  19.  */
  20. package io.wcm.sling.commons.request;

  21. import java.lang.reflect.Array;
  22. import java.util.ArrayList;
  23. import java.util.List;
  24. import java.util.Map;

  25. import org.jetbrains.annotations.NotNull;
  26. import org.jetbrains.annotations.Nullable;
  27. import org.osgi.annotation.versioning.ProviderType;

  28. import io.wcm.sling.commons.util.Escape;

  29. /**
  30.  * Builds a property URL-encoded query string.
  31.  */
  32. @ProviderType
  33. public final class QueryStringBuilder {

  34.   private static final String PARAM_SEPARATOR = "&";
  35.   private static final String VALUE_SEPARATOR = "=";

  36.   private final List<NameValuePair> params = new ArrayList<>();

  37.   /**
  38.    * Add parameter to query string.
  39.    * @param name Parameter name
  40.    * @param value Parameter value. Will be converted to string.
  41.    *          If value is an array or {@link Iterable} the value items will be added as separate parameters.
  42.    * @return this
  43.    */
  44.   @SuppressWarnings("unchecked")
  45.   public @NotNull QueryStringBuilder param(@NotNull String name, @Nullable Object value) {
  46.     if (value instanceof Iterable) {
  47.       Iterable<Object> valueItems = (Iterable)value;
  48.       for (Object valueItem : valueItems) {
  49.         params.add(new NameValuePair(name, valueItem));
  50.       }
  51.     }
  52.     else if (isArray(value)) {
  53.       int length = Array.getLength(value);
  54.       for (int i = 0; i < length; i++) {
  55.         Object valueItem = Array.get(value, i);
  56.         params.add(new NameValuePair(name, valueItem));
  57.       }
  58.     }
  59.     else {
  60.       params.add(new NameValuePair(name, value));
  61.     }
  62.     return this;
  63.   }

  64.   /**
  65.    * Add map of parameters to query string.
  66.    * @param values Map with parameter names and values. Values will be converted to strings.
  67.    *          If a value is an array or {@link Iterable} the value items will be added as separate parameters.
  68.    * @return this
  69.    */
  70.   public @NotNull QueryStringBuilder params(@NotNull Map<String, Object> values) {
  71.     for (Map.Entry<String, Object> entry : values.entrySet()) {
  72.       param(entry.getKey(), entry.getValue());
  73.     }
  74.     return this;
  75.   }

  76.   /**
  77.    * Build query string.
  78.    * @return Query string or null if query string contains no parameters at all.
  79.    */
  80.   public @Nullable String build() {
  81.     StringBuilder queryString = new StringBuilder();

  82.     for (NameValuePair param : params) {
  83.       if (queryString.length() > 0) {
  84.         queryString.append(PARAM_SEPARATOR);
  85.       }
  86.       queryString.append(Escape.urlEncode(param.getName()))
  87.           .append(VALUE_SEPARATOR)
  88.           .append(Escape.urlEncode(param.getValue()));
  89.     }

  90.     if (queryString.length() > 0) {
  91.       return queryString.toString();
  92.     }
  93.     else {
  94.       return null;
  95.     }
  96.   }

  97.   private static boolean isArray(Object value) {
  98.     return value != null && value.getClass().isArray();
  99.   }

  100.   private static class NameValuePair {

  101.     private final String name;
  102.     private final String value;

  103.     NameValuePair(String name, Object value) {
  104.       this.name = name;
  105.       if (value != null) {
  106.         this.value = value.toString();
  107.       }
  108.       else {
  109.         this.value = "";
  110.       }
  111.     }

  112.     public String getName() {
  113.       return this.name;
  114.     }

  115.     public String getValue() {
  116.       return this.value;
  117.     }

  118.   }

  119. }