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

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new e5aa6d084 Refactor string joining into AppendableJoiner (#1244)
e5aa6d084 is described below

commit e5aa6d0844e17362f7780cd5307dd16fd2681d6b
Author: Gary Gregory <garydgreg...@users.noreply.github.com>
AuthorDate: Thu Jul 11 08:32:58 2024 -0400

    Refactor string joining into AppendableJoiner (#1244)
---
 .../org/apache/commons/lang3/AppendableJoiner.java | 313 ++++++++++++
 .../apache/commons/lang3/reflect/TypeUtils.java    | 553 +++++++++------------
 .../apache/commons/lang3/AppendableJoinerTest.java | 142 ++++++
 3 files changed, 683 insertions(+), 325 deletions(-)

diff --git a/src/main/java/org/apache/commons/lang3/AppendableJoiner.java 
b/src/main/java/org/apache/commons/lang3/AppendableJoiner.java
new file mode 100644
index 000000000..e53288a2f
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/AppendableJoiner.java
@@ -0,0 +1,313 @@
+/*
+ * 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.commons.lang3;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.StringJoiner;
+import java.util.function.Supplier;
+
+import org.apache.commons.lang3.exception.UncheckedException;
+import org.apache.commons.lang3.function.FailableBiConsumer;
+
+/**
+ * Joins an array or {@link Iterable} into an existing {@link Appendable} like 
a {@link StringBuilder}; with the goal for call sites to avoid creating
+ * intermediary Strings. This is like {@link String#join(CharSequence, 
CharSequence...)}, {@link String#join(CharSequence, Iterable)}, and {@link 
StringJoiner}.
+ * <p>
+ * Keep an instance in a (static) variable for efficient joining into an 
{@link Appendable} or {@link StringBuilder} without creating temporary Strings.
+ * </p>
+ * <p>
+ * Use the builder and instance methods to reuse the same kind of joining 
prefix, suffix, delimiter, and string conversion.
+ * </p>
+ * <p>
+ * For example:
+ * </p>
+ *
+ * <pre>{@code
+ * // A reuseable instance
+ * private static final AppendableJoiner<Object> JOINER = 
AppendableJoiner.builder()
+ *     .setPrefix("[")
+ *     .setSuffix("]")
+ *     .setDelimiter(", ")
+ *     .get();
+ * }
+ * ...
+ * // Builds straight into a StringBuilder:
+ * StringBuilder sbuilder = new StringBuilder("1");
+ * JOINER.join(sbuilder, "A", "B");
+ * sbuilder.append("2");
+ * JOINER.join(sbuilder, "C", "D");
+ * sbuilder.append("3");
+ * // Returns "1[A, B]2[C, D]3"
+ * return sbuilder.toString();
+ * }</pre>
+ * <p>
+ * To provide a custom Object element to {@link CharSequence} converter, call 
{@link Builder#setElementAppender(FailableBiConsumer)}, for example:
+ * </p>
+ *
+ * <pre>{@code
+ * private static final AppendableJoiner<Item> JOINER = 
AppendableJoiner.builder()
+ *     .setElementAppender(e -> (a, e) -> a.append(e.getFoo())
+ *                                        a.append(e.getBar())
+ *                                        a.append('!'))
+ *     ...
+ *     .get();
+ * }
+ * }</pre>
+ * <p>
+ * This class is immutable and thread-safe.
+ * </p>
+ *
+ * @param <T> the type of elements to join.
+ * @see Appendable
+ * @see StringBuilder
+ * @see String#join(CharSequence, CharSequence...)
+ * @see String#join(CharSequence, Iterable)
+ * @see StringJoiner
+ * @since 3.15.0
+ */
+public final class AppendableJoiner<T> {
+
+    /**
+     * Builds instances of {@link AppendableJoiner}.
+     *
+     * @param <T> the type of elements to join.
+     */
+    public static final class Builder<T> implements 
Supplier<AppendableJoiner<T>> {
+
+        /** The sequence of characters to be used at the beginning. */
+        private CharSequence prefix;
+
+        /** The sequence of characters to be used at the end. */
+        private CharSequence suffix;
+
+        /** The delimiter that separates each element. */
+        private CharSequence delimiter;
+
+        /** The consumer used to render each element of type {@code T} onto an 
{@link Appendable}. */
+        private FailableBiConsumer<Appendable, T, IOException> appender;
+
+        /**
+         * Constructs a new instance.
+         */
+        Builder() {
+            // empty
+        }
+
+        /**
+         * Gets a new instance of {@link AppendableJoiner}.
+         */
+        @Override
+        public AppendableJoiner<T> get() {
+            return new AppendableJoiner<>(prefix, suffix, delimiter, appender);
+        }
+
+        /**
+         * Sets the delimiter that separates each element.
+         *
+         * @param delimiter The delimiter that separates each element.
+         * @return this instance.
+         */
+        public Builder<T> setDelimiter(final CharSequence delimiter) {
+            this.delimiter = delimiter;
+            return this;
+        }
+
+        /**
+         * Sets the consumer used to render each element of type {@code T} 
onto an {@link Appendable}.
+         *
+         * @param appender The consumer used to render each element of type 
{@code T} onto an {@link Appendable}.
+         * @return this instance.
+         */
+        public Builder<T> setElementAppender(final 
FailableBiConsumer<Appendable, T, IOException> appender) {
+            this.appender = appender;
+            return this;
+        }
+
+        /**
+         * Sets the sequence of characters to be used at the beginning.
+         *
+         * @param prefix The sequence of characters to be used at the 
beginning.
+         * @return this instance.
+         */
+        public Builder<T> setPrefix(final CharSequence prefix) {
+            this.prefix = prefix;
+            return this;
+        }
+
+        /**
+         * Sets the sequence of characters to be used at the end.
+         *
+         * @param suffix The sequence of characters to be used at the end.
+         * @return this instance.
+         */
+        public Builder<T> setSuffix(final CharSequence suffix) {
+            this.suffix = suffix;
+            return this;
+        }
+
+    }
+
+    /**
+     * Creates a new builder.
+     *
+     * @param <T> The type of elements.
+     * @return a new builder.
+     */
+    public static <T> Builder<T> builder() {
+        return new Builder<>();
+    }
+
+    /** Could be public in the future, in some form. */
+    @SafeVarargs
+    static <A extends Appendable, T> A joinA(final A appendable, final 
CharSequence prefix, final CharSequence suffix, final CharSequence delimiter,
+            final FailableBiConsumer<Appendable, T, IOException> appender, 
final T... elements) throws IOException {
+        return joinArray(appendable, prefix, suffix, delimiter, appender, 
elements);
+    }
+
+    private static <A extends Appendable, T> A joinArray(final A appendable, 
final CharSequence prefix, final CharSequence suffix, final CharSequence 
delimiter,
+            final FailableBiConsumer<Appendable, T, IOException> appender, 
final T[] elements) throws IOException {
+        appendable.append(prefix);
+        if (elements != null) {
+            if (elements.length > 0) {
+                appender.accept(appendable, elements[0]);
+            }
+            for (int i = 1; i < elements.length; i++) {
+                appendable.append(delimiter);
+                appender.accept(appendable, elements[i]);
+            }
+        }
+        appendable.append(suffix);
+        return appendable;
+    }
+
+    /** Could be public in the future, in some form. */
+    static <T> StringBuilder joinI(final StringBuilder stringBuilder, final 
CharSequence prefix, final CharSequence suffix, final CharSequence delimiter,
+            final FailableBiConsumer<Appendable, T, IOException> appender, 
final Iterable<T> elements) {
+        try {
+            return joinIterable(stringBuilder, prefix, suffix, delimiter, 
appender, elements);
+        } catch (final IOException e) {
+            // Cannot happen with a StringBuilder.
+            throw new UncheckedException(e);
+        }
+    }
+
+    private static <A extends Appendable, T> A joinIterable(final A 
appendable, final CharSequence prefix, final CharSequence suffix,
+            final CharSequence delimiter, final FailableBiConsumer<Appendable, 
T, IOException> appender, final Iterable<T> elements) throws IOException {
+        appendable.append(prefix);
+        if (elements != null) {
+            final Iterator<T> iterator = elements.iterator();
+            if (iterator.hasNext()) {
+                appender.accept(appendable, iterator.next());
+            }
+            while (iterator.hasNext()) {
+                appendable.append(delimiter);
+                appender.accept(appendable, iterator.next());
+            }
+        }
+        appendable.append(suffix);
+        return appendable;
+    }
+
+    /** Could be public in the future, in some form. */
+    @SafeVarargs
+    static <T> StringBuilder joinSB(final StringBuilder stringBuilder, final 
CharSequence prefix, final CharSequence suffix, final CharSequence delimiter,
+            final FailableBiConsumer<Appendable, T, IOException> appender, 
final T... elements) {
+        try {
+            return joinArray(stringBuilder, prefix, suffix, delimiter, 
appender, elements);
+        } catch (final IOException e) {
+            // Cannot happen with a StringBuilder.
+            throw new UncheckedException(e);
+        }
+    }
+
+    private static CharSequence nonNull(final CharSequence value) {
+        return value != null ? value : StringUtils.EMPTY;
+    }
+
+    /** The sequence of characters to be used at the beginning. */
+    private final CharSequence prefix;
+
+    /** The sequence of characters to be used at the end. */
+    private final CharSequence suffix;
+
+    /** The delimiter that separates each element. */
+    private final CharSequence delimiter;
+
+    private final FailableBiConsumer<Appendable, T, IOException> appender;
+
+    /**
+     * Constructs a new instance.
+     */
+    private AppendableJoiner(final CharSequence prefix, final CharSequence 
suffix, final CharSequence delimiter,
+            final FailableBiConsumer<Appendable, T, IOException> appender) {
+        this.prefix = nonNull(prefix);
+        this.suffix = nonNull(suffix);
+        this.delimiter = nonNull(delimiter);
+        this.appender = appender != null ? appender : (a, e) -> 
a.append(String.valueOf(e));
+    }
+
+    /**
+     * Joins stringified objects from the given Iterable into a StringBuilder.
+     *
+     * @param stringBuilder The target.
+     * @param elements      The source.
+     * @return The given StringBuilder.
+     */
+    public StringBuilder join(final StringBuilder stringBuilder, final 
Iterable<T> elements) {
+        return joinI(stringBuilder, prefix, suffix, delimiter, appender, 
elements);
+    }
+
+    /**
+     * Joins stringified objects from the given array into a StringBuilder.
+     *
+     * @param stringBuilder The target.
+     * @param elements      The source.
+     * @return the given target StringBuilder.
+     */
+    public StringBuilder join(final StringBuilder stringBuilder, 
@SuppressWarnings("unchecked") final T... elements) {
+        return joinSB(stringBuilder, prefix, suffix, delimiter, appender, 
elements);
+    }
+
+    /**
+     * Joins stringified objects from the given Iterable into an Appendable.
+     *
+     * @param <A>        the Appendable type.
+     * @param appendable The target.
+     * @param elements   The source.
+     * @return The given StringBuilder.
+     * @throws IOException If an I/O error occurs
+     */
+    public <A extends Appendable> A joinA(final A appendable, final 
Iterable<T> elements) throws IOException {
+        return joinIterable(appendable, prefix, suffix, delimiter, appender, 
elements);
+    }
+
+    /**
+     * Joins stringified objects from the given array into an Appendable.
+     *
+     * @param <A>        the Appendable type.
+     * @param appendable The target.
+     * @param elements   The source.
+     * @return The given StringBuilder.
+     * @throws IOException If an I/O error occurs
+     */
+    public <A extends Appendable> A joinA(final A appendable, 
@SuppressWarnings("unchecked") final T... elements) throws IOException {
+        return joinA(appendable, prefix, suffix, delimiter, appender, 
elements);
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java 
b/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java
index 9d4fbe1af..21b583e8f 100644
--- a/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java
+++ b/src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java
@@ -35,6 +35,7 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.TreeSet;
 
+import org.apache.commons.lang3.AppendableJoiner;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.ObjectUtils;
@@ -42,13 +43,44 @@ import org.apache.commons.lang3.Validate;
 import org.apache.commons.lang3.builder.Builder;
 
 /**
- * Utility methods focusing on type inspection, particularly with regard to
- * generics.
+ * Utility methods focusing on type inspection, particularly with regard to 
generics.
  *
  * @since 3.0
  */
 public class TypeUtils {
 
+    /**
+     * Ampersand sign joiner.
+     */
+    // @formatter:off
+    private static final AppendableJoiner<Type> AMP_JOINER = 
AppendableJoiner.<Type>builder()
+            .setDelimiter(" & ")
+            .setElementAppender((a, e) -> a.append(TypeUtils.toString(e)))
+            .get();
+    // @formatter:on
+
+    /**
+     * Method classToString joiner.
+     */
+    // @formatter:off
+    private static final AppendableJoiner<TypeVariable<Class<?>>> CTJ_JOINER = 
AppendableJoiner.<TypeVariable<Class<?>>>builder()
+        .setDelimiter(", ")
+        .setElementAppender((a, e) -> a.append(TypeUtils.anyToString(e)))
+        .get();
+    // @formatter:on
+
+    /**
+     * Greater than and lesser than sign joiner.
+     */
+    // @formatter:off
+    private static final AppendableJoiner<Object> GT_JOINER = 
AppendableJoiner.builder()
+            .setPrefix("<")
+            .setSuffix(">")
+            .setDelimiter(", ")
+            .setElementAppender((a, e) -> a.append(TypeUtils.anyToString(e)))
+            .get();
+    // @formatter:on
+
     /**
      * GenericArrayType implementation class.
      */
@@ -57,6 +89,7 @@ public class TypeUtils {
 
         /**
          * Constructor
+         *
          * @param componentType of this array type
          */
         private GenericArrayTypeImpl(final Type componentType) {
@@ -108,8 +141,9 @@ public class TypeUtils {
 
         /**
          * Constructor
-         * @param rawClass type
-         * @param useOwner owner type to use, if any
+         *
+         * @param rawClass      type
+         * @param useOwner      owner type to use, if any
          * @param typeArguments formal type arguments
          */
         private ParameterizedTypeImpl(final Class<?> rawClass, final Type 
useOwner, final Type[] typeArguments) {
@@ -182,6 +216,7 @@ public class TypeUtils {
         private Type[] upperBounds;
 
         private Type[] lowerBounds;
+
         /**
          * Constructor
          */
@@ -198,6 +233,7 @@ public class TypeUtils {
 
         /**
          * Specify lower bounds of the wildcard type to build.
+         *
          * @param bounds to set
          * @return {@code this}
          */
@@ -208,6 +244,7 @@ public class TypeUtils {
 
         /**
          * Specify upper bounds of the wildcard type to build.
+         *
          * @param bounds to set
          * @return {@code this}
          */
@@ -226,6 +263,7 @@ public class TypeUtils {
 
         /**
          * Constructor
+         *
          * @param upperBounds of this type
          * @param lowerBounds of this type
          */
@@ -286,36 +324,14 @@ public class TypeUtils {
      */
     public static final WildcardType WILDCARD_ALL = 
wildcardType().withUpperBounds(Object.class).build();
 
-    /**
-     * Appends {@code types} to {@code builder} with separator {@code sep}.
-     *
-     * @param builder destination
-     * @param sep separator
-     * @param types to append
-     * @return {@code builder}
-     */
-    private static <T> StringBuilder appendAllTo(final StringBuilder builder, 
final String sep,
-        @SuppressWarnings("unchecked") final T... types) {
-        Validate.notEmpty(Validate.noNullElements(types));
-        if (types.length > 0) {
-            builder.append(toString(types[0]));
-            for (int i = 1; i < types.length; i++) {
-                builder.append(sep).append(toString(types[i]));
-            }
-        }
-        return builder;
-    }
-
-    private static void appendRecursiveTypes(final StringBuilder builder, 
final int[] recursiveTypeIndexes,
-        final Type[] argumentTypes) {
+    private static void appendRecursiveTypes(final StringBuilder builder, 
final int[] recursiveTypeIndexes, final Type[] argumentTypes) {
         for (int i = 0; i < recursiveTypeIndexes.length; i++) {
-            appendAllTo(builder.append('<'), ", ", 
argumentTypes[i].toString()).append('>');
+            // toString() or SO
+            GT_JOINER.join(builder, argumentTypes[i].toString());
         }
-
         final Type[] argumentsFiltered = ArrayUtils.removeAll(argumentTypes, 
recursiveTypeIndexes);
-
         if (argumentsFiltered.length > 0) {
-            appendAllTo(builder.append('<'), ", ", 
argumentsFiltered).append('>');
+            GT_JOINER.join(builder, (Object[]) argumentsFiltered);
         }
     }
 
@@ -325,7 +341,7 @@ public class TypeUtils {
      * @param cls {@link Class} to format
      * @return String
      */
-    private static String classToString(final Class<?> cls) {
+    private static <T> String classToString(final Class<T> cls) {
         if (cls.isArray()) {
             return toString(cls.getComponentType()) + "[]";
         }
@@ -339,9 +355,8 @@ public class TypeUtils {
             buf.append(cls.getName());
         }
         if (cls.getTypeParameters().length > 0) {
-            buf.append('<');
-            appendAllTo(buf, ", ", cls.getTypeParameters());
-            buf.append('>');
+            // AppendableJoiner.joinSB(buf, null, null, ", ", 
TypeUtils::anyToString, cls.getTypeParameters());
+            CTJ_JOINER.join(buf, (TypeVariable[]) cls.getTypeParameters());
         }
         return buf.toString();
     }
@@ -370,8 +385,7 @@ public class TypeUtils {
         }
         if (type instanceof WildcardType) {
             final WildcardType wild = (WildcardType) type;
-            return containsTypeVariables(getImplicitLowerBounds(wild)[0])
-                || containsTypeVariables(getImplicitUpperBounds(wild)[0]);
+            return containsTypeVariables(getImplicitLowerBounds(wild)[0]) || 
containsTypeVariables(getImplicitUpperBounds(wild)[0]);
         }
         if (type instanceof GenericArrayType) {
             return containsTypeVariables(((GenericArrayType) 
type).getGenericComponentType());
@@ -379,45 +393,32 @@ public class TypeUtils {
         return false;
     }
 
-    private static boolean containsVariableTypeSameParametrizedTypeBound(final 
TypeVariable<?> typeVariable,
-        final ParameterizedType parameterizedType) {
+    private static boolean containsVariableTypeSameParametrizedTypeBound(final 
TypeVariable<?> typeVariable, final ParameterizedType parameterizedType) {
         return ArrayUtils.contains(typeVariable.getBounds(), 
parameterizedType);
     }
 
     /**
-     * Tries to determine the type arguments of a class/interface based on a
-     * super parameterized type's type arguments. This method is the inverse of
-     * {@link #getTypeArguments(Type, Class)} which gets a class/interface's
-     * type arguments based on a subtype. It is far more limited in determining
-     * the type arguments for the subject class's type variables in that it can
-     * only determine those parameters that map from the subject {@link Class}
-     * object to the supertype.
+     * Tries to determine the type arguments of a class/interface based on a 
super parameterized type's type arguments. This method is the inverse of
+     * {@link #getTypeArguments(Type, Class)} which gets a class/interface's 
type arguments based on a subtype. It is far more limited in determining the 
type
+     * arguments for the subject class's type variables in that it can only 
determine those parameters that map from the subject {@link Class} object to the
+     * supertype.
      *
      * <p>
-     * Example: {@link java.util.TreeSet
-     * TreeSet} sets its parameter as the parameter for
-     * {@link java.util.NavigableSet NavigableSet}, which in turn sets the
-     * parameter of {@link java.util.SortedSet}, which in turn sets the
-     * parameter of {@link Set}, which in turn sets the parameter of
-     * {@link java.util.Collection}, which in turn sets the parameter of
-     * {@link Iterable}. Since {@link TreeSet}'s parameter maps
-     * (indirectly) to {@link Iterable}'s parameter, it will be able to
-     * determine that based on the super type {@code Iterable<? extends
-     * Map<Integer, ? extends Collection<?>>>}, the parameter of
-     * {@link TreeSet} is {@code ? extends Map<Integer, ? extends
+     * Example: {@link java.util.TreeSet TreeSet} sets its parameter as the 
parameter for {@link java.util.NavigableSet NavigableSet}, which in turn sets 
the
+     * parameter of {@link java.util.SortedSet}, which in turn sets the 
parameter of {@link Set}, which in turn sets the parameter of
+     * {@link java.util.Collection}, which in turn sets the parameter of 
{@link Iterable}. Since {@link TreeSet}'s parameter maps (indirectly) to
+     * {@link Iterable}'s parameter, it will be able to determine that based 
on the super type {@code Iterable<? extends
+     * Map<Integer, ? extends Collection<?>>>}, the parameter of {@link 
TreeSet} is {@code ? extends Map<Integer, ? extends
      * Collection<?>>}.
      * </p>
      *
-     * @param cls the class whose type parameters are to be determined, not 
{@code null}
-     * @param superParameterizedType the super type from which {@code cls}'s 
type
-     * arguments are to be determined, not {@code null}
-     * @return a {@link Map} of the type assignments that could be determined
-     * for the type variables in each type in the inheritance hierarchy from
-     * {@code type} to {@code toClass} inclusive.
+     * @param cls                    the class whose type parameters are to be 
determined, not {@code null}
+     * @param superParameterizedType the super type from which {@code cls}'s 
type arguments are to be determined, not {@code null}
+     * @return a {@link Map} of the type assignments that could be determined 
for the type variables in each type in the inheritance hierarchy from {@code 
type}
+     *         to {@code toClass} inclusive.
      * @throws NullPointerException if either {@code cls} or {@code 
superParameterizedType} is {@code null}
      */
-    public static Map<TypeVariable<?>, Type> determineTypeArguments(final 
Class<?> cls,
-            final ParameterizedType superParameterizedType) {
+    public static Map<TypeVariable<?>, Type> determineTypeArguments(final 
Class<?> cls, final ParameterizedType superParameterizedType) {
         Objects.requireNonNull(cls, "cls");
         Objects.requireNonNull(superParameterizedType, 
"superParameterizedType");
 
@@ -455,26 +456,24 @@ public class TypeUtils {
      * Tests whether {@code t} equals {@code a}.
      *
      * @param genericArrayType LHS
-     * @param type RHS
+     * @param type             RHS
      * @return boolean
      */
     private static boolean equals(final GenericArrayType genericArrayType, 
final Type type) {
-        return type instanceof GenericArrayType
-            && equals(genericArrayType.getGenericComponentType(), 
((GenericArrayType) type).getGenericComponentType());
+        return type instanceof GenericArrayType && 
equals(genericArrayType.getGenericComponentType(), ((GenericArrayType) 
type).getGenericComponentType());
     }
 
     /**
      * Tests whether {@code t} equals {@code p}.
      *
      * @param parameterizedType LHS
-     * @param type RHS
+     * @param type              RHS
      * @return boolean
      */
     private static boolean equals(final ParameterizedType parameterizedType, 
final Type type) {
         if (type instanceof ParameterizedType) {
             final ParameterizedType other = (ParameterizedType) type;
-            if (equals(parameterizedType.getRawType(), other.getRawType())
-                && equals(parameterizedType.getOwnerType(), 
other.getOwnerType())) {
+            if (equals(parameterizedType.getRawType(), other.getRawType()) && 
equals(parameterizedType.getOwnerType(), other.getOwnerType())) {
                 return equals(parameterizedType.getActualTypeArguments(), 
other.getActualTypeArguments());
             }
         }
@@ -528,14 +527,14 @@ public class TypeUtils {
      * Tests whether {@code t} equals {@code w}.
      *
      * @param wildcardType LHS
-     * @param type RHS
+     * @param type         RHS
      * @return boolean
      */
     private static boolean equals(final WildcardType wildcardType, final Type 
type) {
         if (type instanceof WildcardType) {
             final WildcardType other = (WildcardType) type;
             return equals(getImplicitLowerBounds(wildcardType), 
getImplicitLowerBounds(other))
-                && equals(getImplicitUpperBounds(wildcardType), 
getImplicitUpperBounds(other));
+                    && equals(getImplicitUpperBounds(wildcardType), 
getImplicitUpperBounds(other));
         }
         return false;
     }
@@ -543,7 +542,7 @@ public class TypeUtils {
     /**
      * Helper method to establish the formal parameters for a parameterized 
type.
      *
-     * @param mappings map containing the assignments
+     * @param mappings  map containing the assignments
      * @param variables expected map keys
      * @return array of map values corresponding to specified keys
      */
@@ -558,12 +557,11 @@ public class TypeUtils {
     }
 
     private static int[] findRecursiveTypes(final ParameterizedType 
parameterizedType) {
-        final Type[] filteredArgumentTypes = 
Arrays.copyOf(parameterizedType.getActualTypeArguments(),
-            parameterizedType.getActualTypeArguments().length);
+        final Type[] filteredArgumentTypes = 
Arrays.copyOf(parameterizedType.getActualTypeArguments(), 
parameterizedType.getActualTypeArguments().length);
         int[] indexesToRemove = {};
         for (int i = 0; i < filteredArgumentTypes.length; i++) {
-            if (filteredArgumentTypes[i] instanceof TypeVariable<?> && 
containsVariableTypeSameParametrizedTypeBound(
-                (TypeVariable<?>) filteredArgumentTypes[i], 
parameterizedType)) {
+            if (filteredArgumentTypes[i] instanceof TypeVariable<?>
+                    && 
containsVariableTypeSameParametrizedTypeBound((TypeVariable<?>) 
filteredArgumentTypes[i], parameterizedType)) {
                 indexesToRemove = ArrayUtils.add(indexesToRemove, i);
             }
         }
@@ -573,8 +571,7 @@ public class TypeUtils {
     /**
      * Creates a generic array type instance.
      *
-     * @param componentType the type of the elements of the array. For example 
the component type of {@code boolean[]}
-     *                      is {@code boolean}
+     * @param componentType the type of the elements of the array. For example 
the component type of {@code boolean[]} is {@code boolean}
      * @return {@link GenericArrayType}
      * @since 3.2
      */
@@ -610,10 +607,9 @@ public class TypeUtils {
     }
 
     /**
-     * Gets the closest parent type to the
-     * super class specified by {@code superClass}.
+     * Gets the closest parent type to the super class specified by {@code 
superClass}.
      *
-     * @param cls the class in question
+     * @param cls        the class in question
      * @param superClass the super class
      * @return the closes parent type
      */
@@ -634,14 +630,12 @@ public class TypeUtils {
                 } else if (midType instanceof Class<?>) {
                     midClass = (Class<?>) midType;
                 } else {
-                    throw new IllegalStateException("Unexpected generic"
-                            + " interface type found: " + midType);
+                    throw new IllegalStateException("Unexpected generic" + " 
interface type found: " + midType);
                 }
 
                 // check if this interface is further up the inheritance chain
                 // than the previously found match
-                if (isAssignable(midClass, superClass)
-                        && isAssignable(genericInterface, (Type) midClass)) {
+                if (isAssignable(midClass, superClass) && 
isAssignable(genericInterface, (Type) midClass)) {
                     genericInterface = midType;
                 }
             }
@@ -658,10 +652,8 @@ public class TypeUtils {
     }
 
     /**
-     * Gets an array containing the sole type of {@link Object} if
-     * {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it
-     * returns the result of {@link TypeVariable#getBounds()} passed into
-     * {@link #normalizeUpperBounds}.
+     * Gets an array containing the sole type of {@link Object} if {@link 
TypeVariable#getBounds()} returns an empty array. Otherwise, it returns the 
result of
+     * {@link TypeVariable#getBounds()} passed into {@link 
#normalizeUpperBounds}.
      *
      * @param typeVariable the subject type variable, not {@code null}
      * @return a non-empty array containing the bounds of the type variable.
@@ -675,13 +667,11 @@ public class TypeUtils {
     }
 
     /**
-     * Gets an array containing a single value of {@code null} if
-     * {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise,
-     * it returns the result of {@link WildcardType#getLowerBounds()}.
+     * Gets an array containing a single value of {@code null} if {@link 
WildcardType#getLowerBounds()} returns an empty array. Otherwise, it returns 
the result
+     * of {@link WildcardType#getLowerBounds()}.
      *
      * @param wildcardType the subject wildcard type, not {@code null}
-     * @return a non-empty array containing the lower bounds of the wildcard
-     * type.
+     * @return a non-empty array containing the lower bounds of the wildcard 
type.
      * @throws NullPointerException if {@code wildcardType} is {@code null}
      */
     public static Type[] getImplicitLowerBounds(final WildcardType 
wildcardType) {
@@ -692,14 +682,11 @@ public class TypeUtils {
     }
 
     /**
-     * Gets an array containing the sole value of {@link Object} if
-     * {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise,
-     * it returns the result of {@link WildcardType#getUpperBounds()}
-     * passed into {@link #normalizeUpperBounds}.
+     * Gets an array containing the sole value of {@link Object} if {@link 
WildcardType#getUpperBounds()} returns an empty array. Otherwise, it returns the
+     * result of {@link WildcardType#getUpperBounds()} passed into {@link 
#normalizeUpperBounds}.
      *
      * @param wildcardType the subject wildcard type, not {@code null}
-     * @return a non-empty array containing the upper bounds of the wildcard
-     * type.
+     * @return a non-empty array containing the upper bounds of the wildcard 
type.
      * @throws NullPointerException if {@code wildcardType} is {@code null}
      */
     public static Type[] getImplicitUpperBounds(final WildcardType 
wildcardType) {
@@ -732,16 +719,13 @@ public class TypeUtils {
     }
 
     /**
-     * Gets the raw type of a Java type, given its context. Primarily for use
-     * with {@link TypeVariable}s and {@link GenericArrayType}s, or when you do
-     * not know the runtime type of {@code type}: if you know you have a
-     * {@link Class} instance, it is already raw; if you know you have a
-     * {@link ParameterizedType}, its raw type is only a method call away.
+     * Gets the raw type of a Java type, given its context. Primarily for use 
with {@link TypeVariable}s and {@link GenericArrayType}s, or when you do not 
know
+     * the runtime type of {@code type}: if you know you have a {@link Class} 
instance, it is already raw; if you know you have a {@link ParameterizedType}, 
its
+     * raw type is only a method call away.
      *
-     * @param type to resolve
+     * @param type          to resolve
      * @param assigningType type to be resolved against
-     * @return the resolved {@link Class} object or {@code null} if
-     * the type could not be resolved
+     * @return the resolved {@link Class} object or {@code null} if the type 
could not be resolved
      */
     public static Class<?> getRawType(final Type type, final Type 
assigningType) {
         if (type instanceof Class<?>) {
@@ -770,8 +754,7 @@ public class TypeUtils {
 
             // get the type arguments for the declaring class/interface based
             // on the enclosing type
-            final Map<TypeVariable<?>, Type> typeVarAssigns = 
getTypeArguments(assigningType,
-                    (Class<?>) genericDeclaration);
+            final Map<TypeVariable<?>, Type> typeVarAssigns = 
getTypeArguments(assigningType, (Class<?>) genericDeclaration);
 
             // enclosingType has to be a subclass (or subinterface) of the
             // declaring type
@@ -792,8 +775,7 @@ public class TypeUtils {
 
         if (type instanceof GenericArrayType) {
             // get raw component type
-            final Class<?> rawComponentType = getRawType(((GenericArrayType) 
type)
-                    .getGenericComponentType(), assigningType);
+            final Class<?> rawComponentType = getRawType(((GenericArrayType) 
type).getGenericComponentType(), assigningType);
 
             // create array type from raw component type and return its class
             return rawComponentType != null ? 
Array.newInstance(rawComponentType, 0).getClass() : null;
@@ -810,13 +792,12 @@ public class TypeUtils {
     /**
      * Gets a map of the type arguments of a class in the context of {@code 
toClass}.
      *
-     * @param cls the class in question
-     * @param toClass the context class
+     * @param cls               the class in question
+     * @param toClass           the context class
      * @param subtypeVarAssigns a map with type variables
      * @return the {@link Map} with type arguments
      */
-    private static Map<TypeVariable<?>, Type> getTypeArguments(Class<?> cls, 
final Class<?> toClass,
-            final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
+    private static Map<TypeVariable<?>, Type> getTypeArguments(Class<?> cls, 
final Class<?> toClass, final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
         // make sure they're assignable
         if (!isAssignable(cls, toClass)) {
             return null;
@@ -836,8 +817,7 @@ public class TypeUtils {
         }
 
         // create a copy of the incoming map, or an empty one if it's null
-        final HashMap<TypeVariable<?>, Type> typeVarAssigns = 
subtypeVarAssigns == null ? new HashMap<>()
-                : new HashMap<>(subtypeVarAssigns);
+        final HashMap<TypeVariable<?>, Type> typeVarAssigns = 
subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>(subtypeVarAssigns);
 
         // has target class been reached?
         if (toClass.equals(cls)) {
@@ -849,16 +829,11 @@ public class TypeUtils {
     }
 
     /**
-     * Gets all the type arguments for this parameterized type
-     * including owner hierarchy arguments such as
-     * {@code Outer<K, V>.Inner<T>.DeepInner<E>} .
-     * The arguments are returned in a
-     * {@link Map} specifying the argument type for each {@link TypeVariable}.
+     * Gets all the type arguments for this parameterized type including owner 
hierarchy arguments such as {@code Outer<K, V>.Inner<T>.DeepInner<E>} . The
+     * arguments are returned in a {@link Map} specifying the argument type 
for each {@link TypeVariable}.
      *
-     * @param type specifies the subject parameterized type from which to
-     *             harvest the parameters.
-     * @return a {@link Map} of the type arguments to their respective type
-     * variables.
+     * @param type specifies the subject parameterized type from which to 
harvest the parameters.
+     * @return a {@link Map} of the type arguments to their respective type 
variables.
      */
     public static Map<TypeVariable<?>, Type> getTypeArguments(final 
ParameterizedType type) {
         return getTypeArguments(type, getRawType(type), null);
@@ -868,12 +843,11 @@ public class TypeUtils {
      * Gets a map of the type arguments of a parameterized type in the context 
of {@code toClass}.
      *
      * @param parameterizedType the parameterized type
-     * @param toClass the class
+     * @param toClass           the class
      * @param subtypeVarAssigns a map with type variables
      * @return the {@link Map} with type arguments
      */
-    private static Map<TypeVariable<?>, Type> getTypeArguments(
-            final ParameterizedType parameterizedType, final Class<?> toClass,
+    private static Map<TypeVariable<?>, Type> getTypeArguments(final 
ParameterizedType parameterizedType, final Class<?> toClass,
             final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
         final Class<?> cls = getRawType(parameterizedType);
 
@@ -888,12 +862,10 @@ public class TypeUtils {
         if (ownerType instanceof ParameterizedType) {
             // get the owner type arguments first
             final ParameterizedType parameterizedOwnerType = 
(ParameterizedType) ownerType;
-            typeVarAssigns = getTypeArguments(parameterizedOwnerType,
-                    getRawType(parameterizedOwnerType), subtypeVarAssigns);
+            typeVarAssigns = getTypeArguments(parameterizedOwnerType, 
getRawType(parameterizedOwnerType), subtypeVarAssigns);
         } else {
             // no owner, prep the type variable assignments map
-            typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>()
-                    : new HashMap<>(subtypeVarAssigns);
+            typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>() : new 
HashMap<>(subtypeVarAssigns);
         }
 
         // get the subject parameterized type's arguments
@@ -904,10 +876,7 @@ public class TypeUtils {
         // map the arguments to their respective type variables
         for (int i = 0; i < typeParams.length; i++) {
             final Type typeArg = typeArgs[i];
-            typeVarAssigns.put(
-                    typeParams[i],
-                    typeVarAssigns.getOrDefault(typeArg, typeArg)
-            );
+            typeVarAssigns.put(typeParams[i], 
typeVarAssigns.getOrDefault(typeArg, typeArg));
         }
 
         if (toClass.equals(cls)) {
@@ -920,42 +889,29 @@ public class TypeUtils {
     }
 
     /**
-     * Gets the type arguments of a class/interface based on a subtype. For
-     * instance, this method will determine that both of the parameters for the
-     * interface {@link Map} are {@link Object} for the subtype
-     * {@link java.util.Properties Properties} even though the subtype does not
-     * directly implement the {@link Map} interface.
+     * Gets the type arguments of a class/interface based on a subtype. For 
instance, this method will determine that both of the parameters for the 
interface
+     * {@link Map} are {@link Object} for the subtype {@link 
java.util.Properties Properties} even though the subtype does not directly 
implement the
+     * {@link Map} interface.
      *
      * <p>
-     * This method returns {@code null} if {@code type} is not assignable to
-     * {@code toClass}. It returns an empty map if none of the classes or
-     * interfaces in its inheritance hierarchy specify any type arguments.
+     * This method returns {@code null} if {@code type} is not assignable to 
{@code toClass}. It returns an empty map if none of the classes or interfaces in
+     * its inheritance hierarchy specify any type arguments.
      * </p>
      *
      * <p>
-     * A side effect of this method is that it also retrieves the type
-     * arguments for the classes and interfaces that are part of the hierarchy
-     * between {@code type} and {@code toClass}. So with the above
-     * example, this method will also determine that the type arguments for
-     * {@link java.util.Hashtable Hashtable} are also both {@link Object}.
-     * In cases where the interface specified by {@code toClass} is
-     * (indirectly) implemented more than once (e.g. where {@code toClass}
-     * specifies the interface {@link Iterable Iterable} and
-     * {@code type} specifies a parameterized type that implements both
-     * {@link java.util.Set Set} and {@link java.util.Collection Collection}),
-     * this method will look at the inheritance hierarchy of only one of the
-     * implementations/subclasses; the first interface encountered that isn't a
-     * subinterface to one of the others in the {@code type} to
-     * {@code toClass} hierarchy.
+     * A side effect of this method is that it also retrieves the type 
arguments for the classes and interfaces that are part of the hierarchy between
+     * {@code type} and {@code toClass}. So with the above example, this 
method will also determine that the type arguments for {@link 
java.util.Hashtable
+     * Hashtable} are also both {@link Object}. In cases where the interface 
specified by {@code toClass} is (indirectly) implemented more than once (e.g. 
where
+     * {@code toClass} specifies the interface {@link Iterable Iterable} and 
{@code type} specifies a parameterized type that implements both
+     * {@link java.util.Set Set} and {@link java.util.Collection Collection}), 
this method will look at the inheritance hierarchy of only one of the
+     * implementations/subclasses; the first interface encountered that isn't 
a subinterface to one of the others in the {@code type} to {@code toClass}
+     * hierarchy.
      * </p>
      *
-     * @param type the type from which to determine the type parameters of
-     * {@code toClass}
-     * @param toClass the class whose type parameters are to be determined 
based
-     * on the subtype {@code type}
-     * @return a {@link Map} of the type assignments for the type variables in
-     * each type in the inheritance hierarchy from {@code type} to
-     * {@code toClass} inclusive.
+     * @param type    the type from which to determine the type parameters of 
{@code toClass}
+     * @param toClass the class whose type parameters are to be determined 
based on the subtype {@code type}
+     * @return a {@link Map} of the type assignments for the type variables in 
each type in the inheritance hierarchy from {@code type} to {@code toClass}
+     *         inclusive.
      */
     public static Map<TypeVariable<?>, Type> getTypeArguments(final Type type, 
final Class<?> toClass) {
         return getTypeArguments(type, toClass, null);
@@ -964,13 +920,12 @@ public class TypeUtils {
     /**
      * Gets a map of the type arguments of {@code type} in the context of 
{@code toClass}.
      *
-     * @param type the type in question
-     * @param toClass the class
+     * @param type              the type in question
+     * @param toClass           the class
      * @param subtypeVarAssigns a map with type variables
      * @return the {@link Map} with type arguments
      */
-    private static Map<TypeVariable<?>, Type> getTypeArguments(final Type 
type, final Class<?> toClass,
-            final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
+    private static Map<TypeVariable<?>, Type> getTypeArguments(final Type 
type, final Class<?> toClass, final Map<TypeVariable<?>, Type> 
subtypeVarAssigns) {
         if (type instanceof Class<?>) {
             return getTypeArguments((Class<?>) type, toClass, 
subtypeVarAssigns);
         }
@@ -980,8 +935,8 @@ public class TypeUtils {
         }
 
         if (type instanceof GenericArrayType) {
-            return getTypeArguments(((GenericArrayType) 
type).getGenericComponentType(), toClass
-                    .isArray() ? toClass.getComponentType() : toClass, 
subtypeVarAssigns);
+            return getTypeArguments(((GenericArrayType) 
type).getGenericComponentType(), toClass.isArray() ? toClass.getComponentType() 
: toClass,
+                    subtypeVarAssigns);
         }
 
         // since wildcard types are not assignable to classes, should this just
@@ -1021,10 +976,9 @@ public class TypeUtils {
     }
 
     /**
-     * Tests if the subject type may be implicitly cast to the target class
-     * following the Java generics rules.
+     * Tests if the subject type may be implicitly cast to the target class 
following the Java generics rules.
      *
-     * @param type the subject type to be assigned to the target type
+     * @param type    the subject type to be assigned to the target type
      * @param toClass the target class
      * @return {@code true} if {@code type} is assignable to {@code toClass}.
      */
@@ -1072,9 +1026,7 @@ public class TypeUtils {
         // are class Object and array classes
         if (type instanceof GenericArrayType) {
             return toClass.equals(Object.class)
-                    || toClass.isArray()
-                    && isAssignable(((GenericArrayType) 
type).getGenericComponentType(), toClass
-                            .getComponentType());
+                    || toClass.isArray() && isAssignable(((GenericArrayType) 
type).getGenericComponentType(), toClass.getComponentType());
         }
 
         // wildcard types are not assignable to a class (though one would think
@@ -1087,17 +1039,14 @@ public class TypeUtils {
     }
 
     /**
-     * Tests if the subject type may be implicitly cast to the target
-     * generic array type following the Java generics rules.
+     * Tests if the subject type may be implicitly cast to the target generic 
array type following the Java generics rules.
      *
-     * @param type the subject type to be assigned to the target type
+     * @param type               the subject type to be assigned to the target 
type
      * @param toGenericArrayType the target generic array type
-     * @param typeVarAssigns a map with type variables
-     * @return {@code true} if {@code type} is assignable to
-     * {@code toGenericArrayType}.
+     * @param typeVarAssigns     a map with type variables
+     * @return {@code true} if {@code type} is assignable to {@code 
toGenericArrayType}.
      */
-    private static boolean isAssignable(final Type type, final 
GenericArrayType toGenericArrayType,
-            final Map<TypeVariable<?>, Type> typeVarAssigns) {
+    private static boolean isAssignable(final Type type, final 
GenericArrayType toGenericArrayType, final Map<TypeVariable<?>, Type> 
typeVarAssigns) {
         if (type == null) {
             return true;
         }
@@ -1119,14 +1068,12 @@ public class TypeUtils {
             final Class<?> cls = (Class<?>) type;
 
             // compare the component types
-            return cls.isArray()
-                    && isAssignable(cls.getComponentType(), toComponentType, 
typeVarAssigns);
+            return cls.isArray() && isAssignable(cls.getComponentType(), 
toComponentType, typeVarAssigns);
         }
 
         if (type instanceof GenericArrayType) {
             // compare the component types
-            return isAssignable(((GenericArrayType) 
type).getGenericComponentType(),
-                    toComponentType, typeVarAssigns);
+            return isAssignable(((GenericArrayType) 
type).getGenericComponentType(), toComponentType, typeVarAssigns);
         }
 
         if (type instanceof WildcardType) {
@@ -1163,16 +1110,14 @@ public class TypeUtils {
     }
 
     /**
-     * Tests if the subject type may be implicitly cast to the target
-     * parameterized type following the Java generics rules.
+     * Tests if the subject type may be implicitly cast to the target 
parameterized type following the Java generics rules.
      *
-     * @param type the subject type to be assigned to the target type
+     * @param type                the subject type to be assigned to the 
target type
      * @param toParameterizedType the target parameterized type
-     * @param typeVarAssigns a map with type variables
+     * @param typeVarAssigns      a map with type variables
      * @return {@code true} if {@code type} is assignable to {@code toType}.
      */
-    private static boolean isAssignable(final Type type, final 
ParameterizedType toParameterizedType,
-            final Map<TypeVariable<?>, Type> typeVarAssigns) {
+    private static boolean isAssignable(final Type type, final 
ParameterizedType toParameterizedType, final Map<TypeVariable<?>, Type> 
typeVarAssigns) {
         if (type == null) {
             return true;
         }
@@ -1212,8 +1157,7 @@ public class TypeUtils {
         }
 
         // get the target type's type arguments including owner type arguments
-        final Map<TypeVariable<?>, Type> toTypeVarAssigns = 
getTypeArguments(toParameterizedType,
-                toClass, typeVarAssigns);
+        final Map<TypeVariable<?>, Type> toTypeVarAssigns = 
getTypeArguments(toParameterizedType, toClass, typeVarAssigns);
 
         // now to check each type argument
         for (final TypeVariable<?> var : toTypeVarAssigns.keySet()) {
@@ -1227,10 +1171,8 @@ public class TypeUtils {
             // parameters must either be absent from the subject type, within
             // the bounds of the wildcard type, or be an exact match to the
             // parameters of the target type.
-            if (fromTypeArg != null && toTypeArg != null
-                    && !toTypeArg.equals(fromTypeArg)
-                    && !(toTypeArg instanceof WildcardType && 
isAssignable(fromTypeArg, toTypeArg,
-                            typeVarAssigns))) {
+            if (fromTypeArg != null && toTypeArg != null && 
!toTypeArg.equals(fromTypeArg)
+                    && !(toTypeArg instanceof WildcardType && 
isAssignable(fromTypeArg, toTypeArg, typeVarAssigns))) {
                 return false;
             }
         }
@@ -1238,12 +1180,10 @@ public class TypeUtils {
     }
 
     /**
-     * Tests if the subject type may be implicitly cast to the target type
-     * following the Java generics rules. If both types are {@link Class}
-     * objects, the method returns the result of
-     * {@link ClassUtils#isAssignable(Class, Class)}.
+     * Tests if the subject type may be implicitly cast to the target type 
following the Java generics rules. If both types are {@link Class} objects, the
+     * method returns the result of {@link ClassUtils#isAssignable(Class, 
Class)}.
      *
-     * @param type the subject type to be assigned to the target type
+     * @param type   the subject type to be assigned to the target type
      * @param toType the target type
      * @return {@code true} if {@code type} is assignable to {@code toType}.
      */
@@ -1252,16 +1192,14 @@ public class TypeUtils {
     }
 
     /**
-     * Tests if the subject type may be implicitly cast to the target type
-     * following the Java generics rules.
+     * Tests if the subject type may be implicitly cast to the target type 
following the Java generics rules.
      *
-     * @param type the subject type to be assigned to the target type
-     * @param toType the target type
+     * @param type           the subject type to be assigned to the target type
+     * @param toType         the target type
      * @param typeVarAssigns optional map of type variable assignments
      * @return {@code true} if {@code type} is assignable to {@code toType}.
      */
-    private static boolean isAssignable(final Type type, final Type toType,
-            final Map<TypeVariable<?>, Type> typeVarAssigns) {
+    private static boolean isAssignable(final Type type, final Type toType, 
final Map<TypeVariable<?>, Type> typeVarAssigns) {
         if (toType == null || toType instanceof Class<?>) {
             return isAssignable(type, (Class<?>) toType);
         }
@@ -1286,17 +1224,14 @@ public class TypeUtils {
     }
 
     /**
-     * Tests if the subject type may be implicitly cast to the target type
-     * variable following the Java generics rules.
+     * Tests if the subject type may be implicitly cast to the target type 
variable following the Java generics rules.
      *
-     * @param type the subject type to be assigned to the target type
+     * @param type           the subject type to be assigned to the target type
      * @param toTypeVariable the target type variable
      * @param typeVarAssigns a map with type variables
-     * @return {@code true} if {@code type} is assignable to
-     * {@code toTypeVariable}.
+     * @return {@code true} if {@code type} is assignable to {@code 
toTypeVariable}.
      */
-    private static boolean isAssignable(final Type type, final TypeVariable<?> 
toTypeVariable,
-            final Map<TypeVariable<?>, Type> typeVarAssigns) {
+    private static boolean isAssignable(final Type type, final TypeVariable<?> 
toTypeVariable, final Map<TypeVariable<?>, Type> typeVarAssigns) {
         if (type == null) {
             return true;
         }
@@ -1325,8 +1260,7 @@ public class TypeUtils {
             }
         }
 
-        if (type instanceof Class<?> || type instanceof ParameterizedType
-                || type instanceof GenericArrayType || type instanceof 
WildcardType) {
+        if (type instanceof Class<?> || type instanceof ParameterizedType || 
type instanceof GenericArrayType || type instanceof WildcardType) {
             return false;
         }
 
@@ -1334,17 +1268,14 @@ public class TypeUtils {
     }
 
     /**
-     * Tests if the subject type may be implicitly cast to the target
-     * wildcard type following the Java generics rules.
+     * Tests if the subject type may be implicitly cast to the target wildcard 
type following the Java generics rules.
      *
-     * @param type the subject type to be assigned to the target type
+     * @param type           the subject type to be assigned to the target type
      * @param toWildcardType the target wildcard type
      * @param typeVarAssigns a map with type variables
-     * @return {@code true} if {@code type} is assignable to
-     * {@code toWildcardType}.
+     * @return {@code true} if {@code type} is assignable to {@code 
toWildcardType}.
      */
-    private static boolean isAssignable(final Type type, final WildcardType 
toWildcardType,
-            final Map<TypeVariable<?>, Type> typeVarAssigns) {
+    private static boolean isAssignable(final Type type, final WildcardType 
toWildcardType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
         if (type == null) {
             return true;
         }
@@ -1403,8 +1334,7 @@ public class TypeUtils {
         for (final Type toBound : toUpperBounds) {
             // if there are assignments for unresolved type variables,
             // now's the time to substitute them.
-            if (!isAssignable(type, substituteTypeVariables(toBound, 
typeVarAssigns),
-                    typeVarAssigns)) {
+            if (!isAssignable(type, substituteTypeVariables(toBound, 
typeVarAssigns), typeVarAssigns)) {
                 return false;
             }
         }
@@ -1412,8 +1342,7 @@ public class TypeUtils {
         for (final Type toBound : toLowerBounds) {
             // if there are assignments for unresolved type variables,
             // now's the time to substitute them.
-            if (!isAssignable(substituteTypeVariables(toBound, 
typeVarAssigns), type,
-                    typeVarAssigns)) {
+            if (!isAssignable(substituteTypeVariables(toBound, 
typeVarAssigns), type, typeVarAssigns)) {
                 return false;
             }
         }
@@ -1439,11 +1368,10 @@ public class TypeUtils {
     }
 
     /**
-     * Tests if the given value can be assigned to the target type
-     * following the Java generics rules.
+     * Tests if the given value can be assigned to the target type following 
the Java generics rules.
      *
      * @param value the value to be checked
-     * @param type the target type
+     * @param type  the target type
      * @return {@code true} if {@code value} is an instance of {@code type}.
      */
     public static boolean isInstance(final Object value, final Type type) {
@@ -1451,20 +1379,19 @@ public class TypeUtils {
             return false;
         }
 
-        return value == null ? !(type instanceof Class<?>) || !((Class<?>) 
type).isPrimitive()
-                : isAssignable(value.getClass(), type, null);
+        return value == null ? !(type instanceof Class<?>) || !((Class<?>) 
type).isPrimitive() : isAssignable(value.getClass(), type, null);
     }
 
     /**
      * Maps type variables.
      *
-     * @param <T> the generic type of the class in question
-     * @param cls the class in question
+     * @param <T>               the generic type of the class in question
+     * @param cls               the class in question
      * @param parameterizedType the parameterized type
-     * @param typeVarAssigns the map to be filled
+     * @param typeVarAssigns    the map to be filled
      */
-    private static <T> void mapTypeVariablesToArguments(final Class<T> cls,
-            final ParameterizedType parameterizedType, final 
Map<TypeVariable<?>, Type> typeVarAssigns) {
+    private static <T> void mapTypeVariablesToArguments(final Class<T> cls, 
final ParameterizedType parameterizedType,
+            final Map<TypeVariable<?>, Type> typeVarAssigns) {
         // capture the type variables from the owner type that have assignments
         final Type ownerType = parameterizedType.getOwnerType();
 
@@ -1484,8 +1411,7 @@ public class TypeUtils {
         final TypeVariable<?>[] typeVars = 
getRawType(parameterizedType).getTypeParameters();
 
         // use List view of type parameters of cls so the contains() method 
can be used:
-        final List<TypeVariable<Class<T>>> typeVarList = Arrays.asList(cls
-                .getTypeParameters());
+        final List<TypeVariable<Class<T>>> typeVarList = 
Arrays.asList(cls.getTypeParameters());
 
         for (int i = 0; i < typeArgs.length; i++) {
             final TypeVariable<?> typeVar = typeVars[i];
@@ -1493,7 +1419,7 @@ public class TypeUtils {
 
             // argument of parameterizedType is a type variable of cls
             if (typeVarList.contains(typeArg)
-            // type variable of parameterizedType has an assignment in
+                    // type variable of parameterizedType has an assignment in
                     // the super type.
                     && typeVarAssigns.containsKey(typeVar)) {
                 // map the assignment to the cls's type variable
@@ -1503,28 +1429,28 @@ public class TypeUtils {
     }
 
     /**
-     * Strips out the redundant upper bound types in type
-     * variable types and wildcard types (or it would with wildcard types if
-     * multiple upper bounds were allowed).
+     * Strips out the redundant upper bound types in type variable types and 
wildcard types (or it would with wildcard types if multiple upper bounds were
+     * allowed).
      *
      * <p>
      * Example, with the variable type declaration:
      * </p>
      *
-     * <pre>&lt;K extends java.util.Collection&lt;String&gt; &amp;
-     * java.util.List&lt;String&gt;&gt;</pre>
+     * <pre>
+     * &lt;K extends java.util.Collection&lt;String&gt; &amp;
+     * java.util.List&lt;String&gt;&gt;
+     * </pre>
      *
      * <p>
-     * since {@link List} is a subinterface of {@link Collection},
-     * this method will return the bounds as if the declaration had been:
+     * since {@link List} is a subinterface of {@link Collection}, this method 
will return the bounds as if the declaration had been:
      * </p>
      *
-     * <pre>&lt;K extends java.util.List&lt;String&gt;&gt;</pre>
+     * <pre>
+     * &lt;K extends java.util.List&lt;String&gt;&gt;
+     * </pre>
      *
-     * @param bounds an array of types representing the upper bounds of either
-     * {@link WildcardType} or {@link TypeVariable}, not {@code null}.
-     * @return an array containing the values from {@code bounds} minus the
-     * redundant types.
+     * @param bounds an array of types representing the upper bounds of either 
{@link WildcardType} or {@link TypeVariable}, not {@code null}.
+     * @return an array containing the values from {@code bounds} minus the 
redundant types.
      * @throws NullPointerException if {@code bounds} is {@code null}
      */
     public static Type[] normalizeUpperBounds(final Type[] bounds) {
@@ -1557,24 +1483,22 @@ public class TypeUtils {
     /**
      * Creates a parameterized type instance.
      *
-     * @param rawClass the raw class to create a parameterized type instance 
for
+     * @param rawClass        the raw class to create a parameterized type 
instance for
      * @param typeVariableMap the map used for parameterization
      * @return {@link ParameterizedType}
      * @throws NullPointerException if either {@code rawClass} or {@code 
typeVariableMap} is {@code null}
      * @since 3.2
      */
-    public static final ParameterizedType parameterize(final Class<?> rawClass,
-        final Map<TypeVariable<?>, Type> typeVariableMap) {
+    public static final ParameterizedType parameterize(final Class<?> 
rawClass, final Map<TypeVariable<?>, Type> typeVariableMap) {
         Objects.requireNonNull(rawClass, "rawClass");
         Objects.requireNonNull(typeVariableMap, "typeVariableMap");
-        return parameterizeWithOwner(null, rawClass,
-            extractTypeArgumentsFrom(typeVariableMap, 
rawClass.getTypeParameters()));
+        return parameterizeWithOwner(null, rawClass, 
extractTypeArgumentsFrom(typeVariableMap, rawClass.getTypeParameters()));
     }
 
     /**
      * Creates a parameterized type instance.
      *
-     * @param rawClass the raw class to create a parameterized type instance 
for
+     * @param rawClass      the raw class to create a parameterized type 
instance for
      * @param typeArguments the types used for parameterization
      * @return {@link ParameterizedType}
      * @throws NullPointerException if {@code rawClass} is {@code null}
@@ -1592,10 +1516,8 @@ public class TypeUtils {
      */
     private static String parameterizedTypeToString(final ParameterizedType 
parameterizedType) {
         final StringBuilder builder = new StringBuilder();
-
         final Type useOwner = parameterizedType.getOwnerType();
         final Class<?> raw = (Class<?>) parameterizedType.getRawType();
-
         if (useOwner == null) {
             builder.append(raw.getName());
         } else {
@@ -1606,50 +1528,43 @@ public class TypeUtils {
             }
             builder.append('.').append(raw.getSimpleName());
         }
-
         final int[] recursiveTypeIndexes = 
findRecursiveTypes(parameterizedType);
-
         if (recursiveTypeIndexes.length > 0) {
             appendRecursiveTypes(builder, recursiveTypeIndexes, 
parameterizedType.getActualTypeArguments());
         } else {
-            appendAllTo(builder.append('<'), ", ", 
parameterizedType.getActualTypeArguments()).append('>');
+            GT_JOINER.join(builder, 
parameterizedType.getActualTypeArguments());
         }
-
         return builder.toString();
     }
 
     /**
      * Creates a parameterized type instance.
      *
-     * @param owner the owning type
-     * @param rawClass the raw class to create a parameterized type instance 
for
+     * @param owner           the owning type
+     * @param rawClass        the raw class to create a parameterized type 
instance for
      * @param typeVariableMap the map used for parameterization
      * @return {@link ParameterizedType}
-     * @throws NullPointerException if either {@code rawClass} or {@code 
typeVariableMap}
-     *  is {@code null}
+     * @throws NullPointerException if either {@code rawClass} or {@code 
typeVariableMap} is {@code null}
      * @since 3.2
      */
-    public static final ParameterizedType parameterizeWithOwner(final Type 
owner, final Class<?> rawClass,
-        final Map<TypeVariable<?>, Type> typeVariableMap) {
+    public static final ParameterizedType parameterizeWithOwner(final Type 
owner, final Class<?> rawClass, final Map<TypeVariable<?>, Type> 
typeVariableMap) {
         Objects.requireNonNull(rawClass, "rawClass");
         Objects.requireNonNull(typeVariableMap, "typeVariableMap");
-        return parameterizeWithOwner(owner, rawClass,
-            extractTypeArgumentsFrom(typeVariableMap, 
rawClass.getTypeParameters()));
+        return parameterizeWithOwner(owner, rawClass, 
extractTypeArgumentsFrom(typeVariableMap, rawClass.getTypeParameters()));
     }
 
     /**
      * Creates a parameterized type instance.
      *
-     * @param owner the owning type
-     * @param rawClass the raw class to create a parameterized type instance 
for
+     * @param owner         the owning type
+     * @param rawClass      the raw class to create a parameterized type 
instance for
      * @param typeArguments the types used for parameterization
      *
      * @return {@link ParameterizedType}
      * @throws NullPointerException if {@code rawClass} is {@code null}
      * @since 3.2
      */
-    public static final ParameterizedType parameterizeWithOwner(final Type 
owner, final Class<?> rawClass,
-        final Type... typeArguments) {
+    public static final ParameterizedType parameterizeWithOwner(final Type 
owner, final Class<?> rawClass, final Type... typeArguments) {
         Objects.requireNonNull(rawClass, "rawClass");
         final Type useOwner;
         if (rawClass.getEnclosingClass() == null) {
@@ -1658,14 +1573,12 @@ public class TypeUtils {
         } else if (owner == null) {
             useOwner = rawClass.getEnclosingClass();
         } else {
-            Validate.isTrue(isAssignable(owner, rawClass.getEnclosingClass()),
-                "%s is invalid owner type for parameterized %s", owner, 
rawClass);
+            Validate.isTrue(isAssignable(owner, rawClass.getEnclosingClass()), 
"%s is invalid owner type for parameterized %s", owner, rawClass);
             useOwner = owner;
         }
         Validate.noNullElements(typeArguments, "null type argument at index 
%s");
-        Validate.isTrue(rawClass.getTypeParameters().length == 
typeArguments.length,
-            "invalid number of type parameters specified: expected %d, got 
%d", rawClass.getTypeParameters().length,
-            typeArguments.length);
+        Validate.isTrue(rawClass.getTypeParameters().length == 
typeArguments.length, "invalid number of type parameters specified: expected 
%d, got %d",
+                rawClass.getTypeParameters().length, typeArguments.length);
 
         return new ParameterizedTypeImpl(rawClass, useOwner, typeArguments);
     }
@@ -1673,7 +1586,7 @@ public class TypeUtils {
     /**
      * Finds the mapping for {@code type} in {@code typeVarAssigns}.
      *
-     * @param type the type to be replaced
+     * @param type           the type to be replaced
      * @param typeVarAssigns the map with type variables
      * @return the replaced type
      * @throws IllegalArgumentException if the type cannot be substituted
@@ -1683,8 +1596,7 @@ public class TypeUtils {
             final Type replacementType = typeVarAssigns.get(type);
 
             if (replacementType == null) {
-                throw new IllegalArgumentException("missing assignment type 
for type variable "
-                        + type);
+                throw new IllegalArgumentException("missing assignment type 
for type variable " + type);
             }
             return replacementType;
         }
@@ -1721,7 +1633,7 @@ public class TypeUtils {
         return 
buf.append(':').append(typeVariableToString(typeVariable)).toString();
     }
 
-    private static <T> String toString(final T object) {
+    private static <T> String anyToString(final T object) {
         return object instanceof Type ? toString((Type) object) : 
object.toString();
     }
 
@@ -1754,17 +1666,12 @@ public class TypeUtils {
     }
 
     /**
-     * Determines whether or not specified types satisfy the bounds of their
-     * mapped type variables. When a type parameter extends another (such as
-     * {@code <T, S extends T>}), uses another as a type parameter (such as
-     * {@code <T, S extends Comparable>>}), or otherwise depends on
-     * another type variable to be specified, the dependencies must be included
-     * in {@code typeVarAssigns}.
+     * Determines whether or not specified types satisfy the bounds of their 
mapped type variables. When a type parameter extends another (such as
+     * {@code <T, S extends T>}), uses another as a type parameter (such as 
{@code <T, S extends Comparable>>}), or otherwise depends on another type 
variable
+     * to be specified, the dependencies must be included in {@code 
typeVarAssigns}.
      *
-     * @param typeVariableMap specifies the potential types to be assigned to 
the
-     * type variables, not {@code null}.
-     * @return whether or not the types can be assigned to their respective 
type
-     * variables.
+     * @param typeVariableMap specifies the potential types to be assigned to 
the type variables, not {@code null}.
+     * @return whether or not the types can be assigned to their respective 
type variables.
      * @throws NullPointerException if {@code typeVariableMap} is {@code null}
      */
     public static boolean typesSatisfyVariables(final Map<TypeVariable<?>, 
Type> typeVariableMap) {
@@ -1776,8 +1683,7 @@ public class TypeUtils {
             final Type type = entry.getValue();
 
             for (final Type bound : getImplicitBounds(typeVar)) {
-                if (!isAssignable(type, substituteTypeVariables(bound, 
typeVariableMap),
-                        typeVariableMap)) {
+                if (!isAssignable(type, substituteTypeVariables(bound, 
typeVariableMap), typeVariableMap)) {
                     return false;
                 }
             }
@@ -1792,20 +1698,20 @@ public class TypeUtils {
      * @return String
      */
     private static String typeVariableToString(final TypeVariable<?> 
typeVariable) {
-        final StringBuilder buf = new StringBuilder(typeVariable.getName());
+        final StringBuilder builder = new 
StringBuilder(typeVariable.getName());
         final Type[] bounds = typeVariable.getBounds();
         if (bounds.length > 0 && !(bounds.length == 1 && 
Object.class.equals(bounds[0]))) {
-            buf.append(" extends ");
-            appendAllTo(buf, " & ", typeVariable.getBounds());
+            builder.append(" extends ");
+            AMP_JOINER.join(builder, typeVariable.getBounds());
         }
-        return buf.toString();
+        return builder.toString();
     }
 
     /**
      * Unrolls variables in a type bounds array.
      *
      * @param typeArguments assignments {@link Map}
-     * @param bounds in which to expand variables
+     * @param bounds        in which to expand variables
      * @return {@code bounds} with any variables reassigned
      */
     private static Type[] unrollBounds(final Map<TypeVariable<?>, Type> 
typeArguments, final Type[] bounds) {
@@ -1823,10 +1729,9 @@ public class TypeUtils {
     }
 
     /**
-     * Looks up {@code typeVariable} in {@code typeVarAssigns} 
<em>transitively</em>, i.e. keep looking until the value
-     * found is <em>not</em> a type variable.
+     * Looks up {@code typeVariable} in {@code typeVarAssigns} 
<em>transitively</em>, i.e. keep looking until the value found is <em>not</em> 
a type variable.
      *
-     * @param typeVariable the type variable to look up
+     * @param typeVariable   the type variable to look up
      * @param typeVarAssigns the map used for the look-up
      * @return Type or {@code null} if some variable was not in the map
      */
@@ -1846,7 +1751,7 @@ public class TypeUtils {
      * Gets a type representing {@code type} with variable assignments 
"unrolled."
      *
      * @param typeArguments as from {@link TypeUtils#getTypeArguments(Type, 
Class)}
-     * @param type the type to unroll variable assignments for
+     * @param type          the type to unroll variable assignments for
      * @return Type
      * @since 3.2
      */
@@ -1879,7 +1784,7 @@ public class TypeUtils {
             if (type instanceof WildcardType) {
                 final WildcardType wild = (WildcardType) type;
                 return 
wildcardType().withUpperBounds(unrollBounds(typeArguments, 
wild.getUpperBounds()))
-                    .withLowerBounds(unrollBounds(typeArguments, 
wild.getLowerBounds())).build();
+                        .withLowerBounds(unrollBounds(typeArguments, 
wild.getLowerBounds())).build();
             }
         }
         return type;
@@ -1902,21 +1807,21 @@ public class TypeUtils {
      * @return String
      */
     private static String wildcardTypeToString(final WildcardType 
wildcardType) {
-        final StringBuilder buf = new StringBuilder().append('?');
+        final StringBuilder builder = new StringBuilder().append('?');
         final Type[] lowerBounds = wildcardType.getLowerBounds();
         final Type[] upperBounds = wildcardType.getUpperBounds();
         if (lowerBounds.length > 1 || lowerBounds.length == 1 && 
lowerBounds[0] != null) {
-            appendAllTo(buf.append(" super "), " & ", lowerBounds);
+            AMP_JOINER.join(builder.append(" super "), lowerBounds);
         } else if (upperBounds.length > 1 || upperBounds.length == 1 && 
!Object.class.equals(upperBounds[0])) {
-            appendAllTo(buf.append(" extends "), " & ", upperBounds);
+            AMP_JOINER.join(builder.append(" extends "), upperBounds);
         }
-        return buf.toString();
+        return builder.toString();
     }
 
     /**
      * Wraps the specified {@link Class} in a {@link Typed} wrapper.
      *
-     * @param <T> generic type
+     * @param <T>  generic type
      * @param type to wrap
      * @return Typed&lt;T&gt;
      * @since 3.2
@@ -1928,7 +1833,7 @@ public class TypeUtils {
     /**
      * Wraps the specified {@link Type} in a {@link Typed} wrapper.
      *
-     * @param <T> inferred generic type
+     * @param <T>  inferred generic type
      * @param type to wrap
      * @return Typed&lt;T&gt;
      * @since 3.2
@@ -1938,12 +1843,10 @@ public class TypeUtils {
     }
 
     /**
-     * {@link TypeUtils} instances should NOT be constructed in standard
-     * programming. Instead, the class should be used as
+     * {@link TypeUtils} instances should NOT be constructed in standard 
programming. Instead, the class should be used as
      * {@code TypeUtils.isAssignable(cls, toClass)}.
      * <p>
-     * This constructor is public to permit tools that require a JavaBean 
instance
-     * to operate.
+     * This constructor is public to permit tools that require a JavaBean 
instance to operate.
      * </p>
      *
      * @deprecated TODO Make private in 4.0.
diff --git a/src/test/java/org/apache/commons/lang3/AppendableJoinerTest.java 
b/src/test/java/org/apache/commons/lang3/AppendableJoinerTest.java
new file mode 100644
index 000000000..cb1fa3574
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/AppendableJoinerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.commons.lang3;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Objects;
+
+import org.apache.commons.lang3.AppendableJoiner.Builder;
+import org.apache.commons.lang3.text.StrBuilder;
+import org.apache.commons.text.TextStringBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+/**
+ * Tests {@link AppendableJoiner}.
+ */
+public class AppendableJoinerTest {
+
+    static class Fixture {
+
+        private final String name;
+
+        Fixture(final String name) {
+            this.name = name;
+        }
+
+        /**
+         * Renders myself onto an Appendable to avoid creating intermediary 
strings.
+         */
+        void render(final Appendable appendable) throws IOException {
+            appendable.append(name);
+            appendable.append('!');
+        }
+    }
+
+    @Test
+    public void testAllBuilderPropertiesStringBuilder() {
+        // @formatter:off
+        final AppendableJoiner<Object> joiner = AppendableJoiner.builder()
+                .setPrefix("<")
+                .setDelimiter(".")
+                .setSuffix(">")
+                .setElementAppender((a, e) -> a.append(String.valueOf(e)))
+                .get();
+        // @formatter:on
+        final StringBuilder sbuilder = new StringBuilder("A");
+        assertEquals("A<B.C>", joiner.join(sbuilder, "B", "C").toString());
+        sbuilder.append("1");
+        assertEquals("A<B.C>1<D.E>", joiner.join(sbuilder, Arrays.asList("D", 
"E")).toString());
+    }
+
+    @Test
+    public void testBuildDefaultStringBuilder() {
+        final Builder<Object> builder = AppendableJoiner.builder();
+        assertNotSame(builder.get(), builder.get());
+        final AppendableJoiner<Object> joiner = builder.get();
+        final StringBuilder sbuilder = new StringBuilder("A");
+        assertEquals("ABC", joiner.join(sbuilder, "B", "C").toString());
+        sbuilder.append("1");
+        assertEquals("ABC1DE", joiner.join(sbuilder, "D", "E").toString());
+    }
+
+    @Test
+    public void testBuilder() {
+        assertNotSame(AppendableJoiner.builder(), AppendableJoiner.builder());
+    }
+
+    @SuppressWarnings("deprecation") // Test own StrBuilder
+    @ParameterizedTest
+    @ValueSource(classes = { StringBuilder.class, StringBuffer.class, 
StringWriter.class, StrBuilder.class, TextStringBuilder.class })
+    public void testDelimiterAppendable(final Class<? extends Appendable> 
clazz) throws Exception {
+        final AppendableJoiner<Object> joiner = 
AppendableJoiner.builder().setDelimiter(".").get();
+        final Appendable sbuilder = clazz.newInstance();
+        sbuilder.append("A");
+        // throws IOException
+        assertEquals("AB.C", joiner.joinA(sbuilder, "B", "C").toString());
+        sbuilder.append("1");
+        // throws IOException
+        assertEquals("AB.C1D.E", joiner.joinA(sbuilder, Arrays.asList("D", 
"E")).toString());
+    }
+
+    @Test
+    public void testDelimiterStringBuilder() {
+        final AppendableJoiner<Object> joiner = 
AppendableJoiner.builder().setDelimiter(".").get();
+        final StringBuilder sbuilder = new StringBuilder("A");
+        // does not throw IOException
+        assertEquals("AB.C", joiner.join(sbuilder, "B", "C").toString());
+        sbuilder.append("1");
+        // does not throw IOException
+        assertEquals("AB.C1D.E", joiner.join(sbuilder, Arrays.asList("D", 
"E")).toString());
+    }
+
+    @Test
+    public void testToCharSequenceStringBuilder1() {
+        // @formatter:off
+        final AppendableJoiner<Object> joiner = AppendableJoiner.builder()
+                .setPrefix("<")
+                .setDelimiter(".")
+                .setSuffix(">")
+                .setElementAppender((a, e) -> 
a.append("|").append(Objects.toString(e)))
+                .get();
+        // @formatter:on
+        final StringBuilder sbuilder = new StringBuilder("A");
+        assertEquals("A<|B.|C>", joiner.join(sbuilder, "B", "C").toString());
+        sbuilder.append("1");
+        assertEquals("A<|B.|C>1<|D.|E>", joiner.join(sbuilder, 
Arrays.asList("D", "E")).toString());
+    }
+
+    @Test
+    public void testToCharSequenceStringBuilder2() {
+        // @formatter:off
+        final AppendableJoiner<Fixture> joiner = 
AppendableJoiner.<Fixture>builder()
+                .setElementAppender((a, e) -> e.render(a))
+                .get();
+        // @formatter:on
+        final StringBuilder sbuilder = new StringBuilder("[");
+        assertEquals("[B!C!", joiner.join(sbuilder, new Fixture("B"), new 
Fixture("C")).toString());
+        sbuilder.append("]");
+        assertEquals("[B!C!]D!E!", joiner.join(sbuilder, Arrays.asList(new 
Fixture("D"), new Fixture("E"))).toString());
+    }
+}

Reply via email to