You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2024/07/11 12:33:03 UTC
(commons-lang) branch master updated: Refactor string joining into AppendableJoiner (#1244)
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 <ga...@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><K extends java.util.Collection<String> &
- * java.util.List<String>></pre>
+ * <pre>
+ * <K extends java.util.Collection<String> &
+ * java.util.List<String>>
+ * </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><K extends java.util.List<String>></pre>
+ * <pre>
+ * <K extends java.util.List<String>>
+ * </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<T>
* @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<T>
* @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());
+ }
+}