You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2018/10/15 11:23:59 UTC

[incubator-servicecomb-java-chassis] 03/06: [SCB-928] support query array param in RPC invoking style

This is an automated email from the ASF dual-hosted git repository.

liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git

commit 67360578dc67e25a3a34fddad3129d41b710bb53
Author: yaohaishi <ya...@huawei.com>
AuthorDate: Fri Oct 12 10:00:41 2018 +0800

    [SCB-928] support query array param in RPC invoking style
---
 .../rest/codec/param/QueryProcessorCreator.java    |   4 +
 .../rest/definition/path/QueryVarParamWriter.java  |  45 +++-
 .../definition/path/QueryVarParamWriterTest.java   | 233 +++++++++++++++++++++
 .../property/SwaggerParamCollectionFormat.java     |  50 ++++-
 .../property/SwaggerParamCollectionFormatTest.java | 116 ++++++++++
 5 files changed, 439 insertions(+), 9 deletions(-)

diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java
index 2e6d941..952c649 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java
@@ -90,6 +90,10 @@ public class QueryProcessorCreator implements ParamValueProcessorCreator {
     public String getProcessorType() {
       return PARAMTYPE;
     }
+
+    public SwaggerParamCollectionFormat getCollectionFormat() {
+      return collectionFormat;
+    }
   }
 
   public QueryProcessorCreator() {
diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.java
index e077429..dd288c0 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.java
@@ -19,18 +19,24 @@ package org.apache.servicecomb.common.rest.definition.path;
 
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.Collection;
 
 import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory;
+import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor;
 import org.apache.servicecomb.common.rest.definition.RestParam;
+import org.apache.servicecomb.swagger.converter.property.SwaggerParamCollectionFormat;
 
 public class QueryVarParamWriter extends AbstractUrlParamWriter {
   // ? or &
   private char prefix;
 
+  private SwaggerParamCollectionFormat collectionFormat;
+
   public QueryVarParamWriter(char prefix, RestParam param) {
     this.param = param;
     this.prefix = prefix;
+    this.collectionFormat = ((QueryProcessor) param.getParamProcessor()).getCollectionFormat();
   }
 
   @Override
@@ -63,6 +69,11 @@ public class QueryVarParamWriter extends AbstractUrlParamWriter {
 
   @SuppressWarnings("unchecked")
   private void writeCollection(StringBuilder builder, Object value) throws Exception {
+    if (shouldJoinParams()) {
+      writeJoinedParams(builder, (Collection<?>) value);
+      return;
+    }
+
     for (Object item : (Collection<Object>) value) {
       writeItem(builder, item);
     }
@@ -73,6 +84,11 @@ public class QueryVarParamWriter extends AbstractUrlParamWriter {
   }
 
   private void writeArray(StringBuilder builder, Object value) throws Exception {
+    if (shouldJoinParams()) {
+      writeJoinedParams(builder, Arrays.asList(((Object[]) value)));
+      return;
+    }
+
     for (Object item : (Object[]) value) {
       writeItem(builder, item);
     }
@@ -82,20 +98,35 @@ public class QueryVarParamWriter extends AbstractUrlParamWriter {
     }
   }
 
+  private void writeJoinedParams(StringBuilder builder, Collection<?> value) throws Exception {
+    String joinedParam = collectionFormat.joinParam(value);
+    if (null == joinedParam) {
+      return;
+    }
+    writeKeyEqual(builder);
+    builder.append(encodeNotNullValue(joinedParam));
+  }
+
+  /**
+   * Whether to join params with separator.
+   * For collection format csv/ssv/tsv/pipes
+   */
+  private boolean shouldJoinParams() {
+    return null != collectionFormat && SwaggerParamCollectionFormat.MULTI != collectionFormat;
+  }
+
   private void deleteLastChar(StringBuilder builder) {
     builder.setLength(builder.length() - 1);
   }
 
   private void writeItem(StringBuilder builder, Object item) throws Exception {
-    writeKeyEqual(builder);
-
-    // TODO:数组元素为null,当前找不到表达方式,通过issue跟踪,有解决方案后再来处理
-    // http://code.huawei.com/CSE/cse-java-chassis/issues/133
-    if (item != null) {
-      builder.append(encodeNotNullValue(item));
+    if (null == item) {
+      builder.append('&');
+      return;
     }
 
-    builder.append('&');
+    writeKeyEqual(builder);
+    builder.append(encodeNotNullValue(item)).append('&');
   }
 
   private String encodeNotNullValue(Object value) throws Exception {
diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java
new file mode 100644
index 0000000..b73fb1d
--- /dev/null
+++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.servicecomb.common.rest.definition.path;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.servicecomb.common.rest.definition.RestParam;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import io.swagger.models.parameters.QueryParameter;
+
+public class QueryVarParamWriterTest {
+
+  private static QueryVarParamWriter queryVarParamWriterCsv;
+
+  private static QueryVarParamWriter queryVarParamWriterMulti;
+
+  private static QueryVarParamWriter queryVarParamWriterDefault;
+
+  @BeforeClass
+  public static void beforeClass() {
+    QueryParameter parameter = new QueryParameter();
+    parameter.setName("q");
+    parameter.setCollectionFormat("csv");
+    queryVarParamWriterCsv = new QueryVarParamWriter(
+        '?',
+        new RestParam(0, parameter, String[].class));
+
+    parameter = new QueryParameter();
+    parameter.setName("q");
+    parameter.setCollectionFormat("multi");
+    queryVarParamWriterMulti = new QueryVarParamWriter(
+        '?',
+        new RestParam(0, parameter, String[].class));
+
+    parameter = new QueryParameter();
+    parameter.setName("q");
+    queryVarParamWriterDefault = new QueryVarParamWriter(
+        '?',
+        new RestParam(0, parameter, String[].class));
+  }
+
+  @Test
+  public void write() throws Exception {
+    StringBuilder stringBuilder = new StringBuilder();
+    Object[] args = {"a"};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=a", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=a", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=a", stringBuilder.toString());
+  }
+
+  @Test
+  public void writeNull() throws Exception {
+    StringBuilder stringBuilder = new StringBuilder();
+    Object[] args = {null};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+  }
+
+  @Test
+  public void writeArray() throws Exception {
+    StringBuilder stringBuilder = new StringBuilder();
+    Object[] args = new Object[] {new String[] {"ab", "cd", "ef"}};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab%2Ccd%2Cef", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.toString());
+
+    // encode space char
+    stringBuilder = new StringBuilder();
+    args[0] = new String[] {"a b", " ", "", "ef"};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b%2C+%2C%2Cef", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.toString());
+
+    // pass blank string
+    stringBuilder = new StringBuilder();
+    args[0] = new String[] {""};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.toString());
+
+    // pass empty
+    stringBuilder = new StringBuilder();
+    args[0] = new String[] {};
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    // pass null
+    args[0] = new String[] {null};
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    args[0] = new String[] {null, "ab", null, "cd", null, null, "", null, "ef", null};
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab%2Ccd%2C%2Cef", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?&q=ab&&q=cd&&&q=&&q=ef&", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?&q=ab&&q=cd&&&q=&&q=ef&", stringBuilder.toString());
+  }
+
+  @Test
+  public void writeList() throws Exception {
+    List<String> queryList = Arrays.asList("ab", "cd", "ef");
+    Object[] args = {queryList};
+    StringBuilder stringBuilder = new StringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab%2Ccd%2Cef", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.toString());
+
+    // encode space char
+    args[0] = Arrays.asList("a b", " ", "", "ef");
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b%2C+%2C%2Cef", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.toString());
+
+    // pass blank string
+    stringBuilder = new StringBuilder();
+    args[0] = Collections.singletonList("");
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?q=", stringBuilder.toString());
+
+    // pass empty
+    stringBuilder = new StringBuilder();
+    args[0] = new ArrayList<>();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    // pass null
+    args[0] = Collections.singletonList(null);
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?", stringBuilder.toString());
+    args[0] = Arrays.asList(null, "ab", null, "cd", null, null, "", null, "ef", null);
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterCsv.write(stringBuilder, args);
+    Assert.assertEquals("?q=ab%2Ccd%2C%2Cef", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterMulti.write(stringBuilder, args);
+    Assert.assertEquals("?&q=ab&&q=cd&&&q=&&q=ef&", stringBuilder.toString());
+    stringBuilder = new StringBuilder();
+    queryVarParamWriterDefault.write(stringBuilder, args);
+    Assert.assertEquals("?&q=ab&&q=cd&&&q=&&q=ef&", stringBuilder.toString());
+  }
+}
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormat.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormat.java
index b354db0..1cca580 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormat.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormat.java
@@ -17,11 +17,22 @@
 
 package org.apache.servicecomb.swagger.converter.property;
 
+import java.util.Collection;
+import java.util.Iterator;
+
 public enum SwaggerParamCollectionFormat {
   CSV("csv", ","),
   SSV("ssv", " "),
   TSV("tsv", "\t"),
-  PIPES("pipes", "\\|"),
+  PIPES("pipes", "|") {
+    @Override
+    public String[] splitParam(String rawParam) {
+      if (null == rawParam) {
+        return new String[0];
+      }
+      return rawParam.split("\\|", -1);
+    }
+  },
   MULTI("multi", null) {
     /**
      * In fact, {@link SwaggerParamCollectionFormat#MULTI#splitParam(String)} of {@link SwaggerParamCollectionFormat#MULTI}
@@ -57,6 +68,41 @@ public enum SwaggerParamCollectionFormat {
     if (null == rawParam) {
       return new String[0];
     }
-    return rawParam.split(separator);
+    return rawParam.split(separator, -1);
+  }
+
+  /**
+   * Join params with {@link #separator}.
+   * Null element will be ignored since {@code null} cannot be described in query array param.
+   *
+   * @return joined params, or return {@code null} if {@code params} is null or all elements of {@code params} are null.
+   */
+  public String joinParam(Collection<?> params) {
+    if (null == params) {
+      return null;
+    }
+    StringBuilder paramBuilder = new StringBuilder();
+    Iterator<?> paramIterator = params.iterator();
+    boolean allNullElement = true;
+    while (paramIterator.hasNext()) {
+      // find the next not-null element
+      Object param = paramIterator.next();
+      while (null == param && paramIterator.hasNext()) {
+        param = paramIterator.next();
+      }
+      if (null == param) {
+        // the rest of all elements are null, no need to go on
+        break;
+      }
+
+      if (allNullElement) {
+        allNullElement = false;
+        paramBuilder.append(param);
+      } else {
+        // There are elements appended into builder before, need a separator
+        paramBuilder.append(separator).append(param);
+      }
+    }
+    return allNullElement ? null : paramBuilder.toString();
   }
 }
diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormatTest.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormatTest.java
index 48d202e..ba5862a 100644
--- a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormatTest.java
+++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/converter/property/SwaggerParamCollectionFormatTest.java
@@ -17,6 +17,15 @@
 
 package org.apache.servicecomb.swagger.converter.property;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
 import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Test;
@@ -80,5 +89,112 @@ public class SwaggerParamCollectionFormatTest {
         Matchers.arrayContaining(""));
     Assert.assertThat(SwaggerParamCollectionFormat.MULTI.splitParam(""),
         Matchers.arrayContaining(""));
+
+    Assert.assertThat(SwaggerParamCollectionFormat.CSV.splitParam("a,,b"),
+        Matchers.arrayContaining("a", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.SSV.splitParam("a  b"),
+        Matchers.arrayContaining("a", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.TSV.splitParam("a\t\tb"),
+        Matchers.arrayContaining("a", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.PIPES.splitParam("a||b"),
+        Matchers.arrayContaining("a", "", "b"));
+
+    Assert.assertThat(SwaggerParamCollectionFormat.CSV.splitParam("a,,"),
+        Matchers.arrayContaining("a", "", ""));
+    Assert.assertThat(SwaggerParamCollectionFormat.SSV.splitParam("a  "),
+        Matchers.arrayContaining("a", "", ""));
+    Assert.assertThat(SwaggerParamCollectionFormat.TSV.splitParam("a\t\t"),
+        Matchers.arrayContaining("a", "", ""));
+    String[] actual = SwaggerParamCollectionFormat.PIPES.splitParam("a||");
+    Assert.assertThat(Arrays.toString(actual), actual,
+        Matchers.arrayContaining("a", "", ""));
+
+    Assert.assertThat(SwaggerParamCollectionFormat.CSV.splitParam(",,b"),
+        Matchers.arrayContaining("", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.SSV.splitParam("  b"),
+        Matchers.arrayContaining("", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.TSV.splitParam("\t\tb"),
+        Matchers.arrayContaining("", "", "b"));
+    Assert.assertThat(SwaggerParamCollectionFormat.PIPES.splitParam("||b"),
+        Matchers.arrayContaining("", "", "b"));
+  }
+
+  @Test
+  public void joinNormal() {
+    List<String> params = Arrays.asList("a", "b", "c");
+    assertEquals("a,b,c", SwaggerParamCollectionFormat.CSV.joinParam(params));
+    assertEquals("a b c", SwaggerParamCollectionFormat.SSV.joinParam(params));
+    assertEquals("a\tb\tc", SwaggerParamCollectionFormat.TSV.joinParam(params));
+    assertEquals("a|b|c", SwaggerParamCollectionFormat.PIPES.joinParam(params));
+  }
+
+  @Test
+  public void join_SingleElement() {
+    List<String> params = Collections.singletonList("a");
+    assertEquals("a", SwaggerParamCollectionFormat.CSV.joinParam(params));
+    assertEquals("a", SwaggerParamCollectionFormat.SSV.joinParam(params));
+    assertEquals("a", SwaggerParamCollectionFormat.TSV.joinParam(params));
+    assertEquals("a", SwaggerParamCollectionFormat.PIPES.joinParam(params));
+  }
+
+  @Test
+  public void join_EmptyArray() {
+    Assert.assertNull(SwaggerParamCollectionFormat.CSV.joinParam(Collections.EMPTY_LIST));
+  }
+
+  @Test
+  public void join_NullAndBlankElement() {
+    Assert.assertNull(SwaggerParamCollectionFormat.CSV.joinParam(Collections.singletonList(null)));
+
+    assertEquals("", SwaggerParamCollectionFormat.CSV.joinParam(Collections.singleton("")));
+    assertEquals("a,,b,c", SwaggerParamCollectionFormat.CSV.joinParam(Arrays.asList("a", "", "b", "c")));
+    assertEquals("a  b c", SwaggerParamCollectionFormat.SSV.joinParam(Arrays.asList("a", "", "b", "c")));
+    assertEquals("a\t\tb\tc", SwaggerParamCollectionFormat.TSV.joinParam(Arrays.asList("a", "", "b", "c")));
+    assertEquals("a||b|c", SwaggerParamCollectionFormat.PIPES.joinParam(Arrays.asList("a", "", "b", "c")));
+
+    assertEquals("a,b,,c",
+        SwaggerParamCollectionFormat.CSV
+            .joinParam(Arrays.asList(null, "a", null, "b", null, "", null, null, "c", null)));
+    assertEquals("a b  c",
+        SwaggerParamCollectionFormat.SSV
+            .joinParam(Arrays.asList(null, "a", null, "b", null, "", null, null, "c", null)));
+    assertEquals("a\tb\t\tc",
+        SwaggerParamCollectionFormat.TSV
+            .joinParam(Arrays.asList(null, "a", null, "b", null, "", null, null, "c", null)));
+    assertEquals("a|b||c",
+        SwaggerParamCollectionFormat.PIPES
+            .joinParam(Arrays.asList(null, "a", null, "b", null, "", null, null, "c", null)));
+
+    assertEquals("a,b,,c",
+        SwaggerParamCollectionFormat.CSV
+            .joinParam(Arrays.asList(null, null, "a", null, "b", null, "", null, null, "c", null, null)));
+    assertEquals("a b  c",
+        SwaggerParamCollectionFormat.SSV
+            .joinParam(Arrays.asList(null, null, "a", null, "b", null, "", null, null, "c", null, null)));
+    assertEquals("a\tb\t\tc",
+        SwaggerParamCollectionFormat.TSV
+            .joinParam(Arrays.asList(null, null, "a", null, "b", null, "", null, null, "c", null, null)));
+    assertEquals("a|b||c",
+        SwaggerParamCollectionFormat.PIPES
+            .joinParam(Arrays.asList(null, null, "a", null, "b", null, "", null, null, "c", null, null)));
+  }
+
+  @Test
+  public void join_NullArray() {
+    assertNull(SwaggerParamCollectionFormat.CSV.joinParam(null));
+  }
+
+  /**
+   * In fact, the {@link SwaggerParamCollectionFormat#joinParam(Collection)} of {@link SwaggerParamCollectionFormat#MULTI}
+   * should never be invoked.
+   * This test is just for ensuring the method does not throw exception.
+   */
+  @Test
+  public void joinMulti() {
+    assertEquals("anullbnullc", SwaggerParamCollectionFormat.MULTI.joinParam(Arrays.asList("a", "b", "c")));
+    assertEquals("a", SwaggerParamCollectionFormat.MULTI.joinParam(Collections.singletonList("a")));
+    assertNull(SwaggerParamCollectionFormat.MULTI.joinParam(new ArrayList<String>()));
+    assertNull(SwaggerParamCollectionFormat.MULTI.joinParam(Collections.singleton(null)));
+    assertNull(SwaggerParamCollectionFormat.MULTI.joinParam(null));
   }
 }
\ No newline at end of file