一、前言
关于使用Feign的feign-core包中feign.Util工具类对常用判空checkNotNull、数组转换处理toArray、字节流转换toByteArray及复制copy等操作,详情参见源码说明部分。
二、源码说明
package feign; @b@import java.io.ByteArrayOutputStream; @b@import java.io.Closeable; @b@import java.io.IOException; @b@import java.io.InputStream; @b@import java.io.OutputStream; @b@import java.io.Reader; @b@import java.lang.reflect.Array; @b@import java.lang.reflect.Method; @b@import java.lang.reflect.Modifier; @b@import java.lang.reflect.ParameterizedType; @b@import java.lang.reflect.Type; @b@import java.lang.reflect.WildcardType; @b@import java.nio.ByteBuffer; @b@import java.nio.CharBuffer; @b@import java.nio.charset.CharacterCodingException; @b@import java.nio.charset.Charset; @b@import java.util.ArrayList; @b@import java.util.Collection; @b@import java.util.Collections; @b@import java.util.Iterator; @b@import java.util.LinkedHashMap; @b@import java.util.List; @b@import java.util.Map; @b@import java.util.NoSuchElementException; @b@import java.util.Set; @b@import static java.lang.String.format; @b@/** @b@ * Utilities, typically copied in from guava, so as to avoid dependency conflicts. @b@ */ @b@public class Util { @b@/** @b@ * The HTTP Content-Length header field name. @b@ */ @b@public static final String CONTENT_LENGTH = "Content-Length"; @b@/** @b@ * The HTTP Content-Encoding header field name. @b@ */ @b@public static final String CONTENT_ENCODING = "Content-Encoding"; @b@/** @b@ * The HTTP Retry-After header field name. @b@ */ @b@public static final String RETRY_AFTER = "Retry-After"; @b@/** @b@ * Value for the Content-Encoding header that indicates that GZIP encoding is in use. @b@ */ @b@public static final String ENCODING_GZIP = "gzip"; @b@/** @b@ * Value for the Content-Encoding header that indicates that DEFLATE encoding is in use. @b@ */ @b@public static final String ENCODING_DEFLATE = "deflate"; @b@/** @b@ * UTF-8: eight-bit UCS Transformation Format. @b@ */ @b@public static final Charset UTF_8 = Charset.forName("UTF-8"); @b@// com.google.common.base.Charsets @b@/** @b@ * ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1). @b@ */ @b@public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); @b@private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes) @b@/** @b@ * Type literal for {@code Map<String, ?>}. @b@ */ @b@public static final Type MAP_STRING_WILDCARD = @b@new Types.ParameterizedTypeImpl(null, Map.class, String.class, @b@new Types.WildcardTypeImpl(new Type[]{Object.class}, new Type[0])); @b@private Util() { // no instances @b@} @b@/** @b@ * Copy of {@code com.google.common.base.Preconditions#checkArgument}. @b@ */ @b@public static void checkArgument(boolean expression, @b@String errorMessageTemplate, @b@Object... errorMessageArgs) { @b@if (!expression) { @b@throw new IllegalArgumentException( @b@format(errorMessageTemplate, errorMessageArgs)); @b@} @b@} @b@/** @b@ * Copy of {@code com.google.common.base.Preconditions#checkNotNull}. @b@ */ @b@public static <T> T checkNotNull(T reference, @b@String errorMessageTemplate, @b@Object... errorMessageArgs) { @b@if (reference == null) { @b@// If either of these parameters is null, the right thing happens anyway @b@throw new NullPointerException( @b@format(errorMessageTemplate, errorMessageArgs)); @b@} @b@return reference; @b@} @b@/** @b@ * Copy of {@code com.google.common.base.Preconditions#checkState}. @b@ */ @b@public static void checkState(boolean expression, @b@String errorMessageTemplate, @b@Object... errorMessageArgs) { @b@if (!expression) { @b@throw new IllegalStateException( @b@format(errorMessageTemplate, errorMessageArgs)); @b@} @b@} @b@/** @b@ * Identifies a method as a default instance method. @b@ */ @b@public static boolean isDefault(Method method) { @b@// Default methods are public non-abstract, non-synthetic, and non-static instance methods @b@// declared in an interface. @b@// method.isDefault() is not sufficient for our usage as it does not check @b@// for synthetic methods. As a result, it picks up overridden methods as well as actual default methods. @b@final int SYNTHETIC = 0x00001000; @b@return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) == @b@Modifier.PUBLIC) && method.getDeclaringClass().isInterface(); @b@} @b@/** @b@ * Adapted from {@code com.google.common.base.Strings#emptyToNull}. @b@ */ @b@public static String emptyToNull(String string) { @b@return string == null || string.isEmpty() ? null : string; @b@} @b@/** @b@ * Adapted from {@code com.google.common.base.Strings#emptyToNull}. @b@ */ @b@@SuppressWarnings("unchecked") @b@public static <T> T[] toArray(Iterable<? extends T> iterable, Class<T> type) { @b@Collection<T> collection; @b@if (iterable instanceof Collection) { @b@collection = (Collection<T>) iterable; @b@} else { @b@collection = new ArrayList<T>(); @b@for (T element : iterable) { @b@collection.add(element); @b@} @b@} @b@T[] array = (T[]) Array.newInstance(type, collection.size()); @b@return collection.toArray(array); @b@} @b@/** @b@ * Returns an unmodifiable collection which may be empty, but is never null. @b@ */ @b@public static <T> Collection<T> valuesOrEmpty(Map<String, Collection<T>> map, String key) { @b@return map.containsKey(key) && map.get(key) != null ? map.get(key) : Collections.<T>emptyList(); @b@} @b@public static void ensureClosed(Closeable closeable) { @b@if (closeable != null) { @b@try { @b@closeable.close(); @b@} catch (IOException ignored) { // NOPMD @b@} @b@} @b@} @b@/** @b@ * Resolves the last type parameter of the parameterized {@code supertype}, based on the {@code @b@ * genericContext}, into its upper bounds. <p/> Implementation copied from {@code @b@ * retrofit.RestMethodInfo}. @b@ * @b@ * @param genericContext Ex. {@link java.lang.reflect.Field#getGenericType()} @b@ * @param supertype Ex. {@code Decoder.class} @b@ * @return in the example above, the type parameter of {@code Decoder}. @b@ * @throws IllegalStateException if {@code supertype} cannot be resolved into a parameterized type @b@ * using {@code context}. @b@ */ @b@public static Type resolveLastTypeParameter(Type genericContext, Class<?> supertype) @b@throws IllegalStateException { @b@Type resolvedSuperType = @b@Types.getSupertype(genericContext, Types.getRawType(genericContext), supertype); @b@checkState(resolvedSuperType instanceof ParameterizedType, @b@"could not resolve %s into a parameterized type %s", @b@genericContext, supertype); @b@Type[] types = ParameterizedType.class.cast(resolvedSuperType).getActualTypeArguments(); @b@for (int i = 0; i < types.length; i++) { @b@Type type = types[i]; @b@if (type instanceof WildcardType) { @b@types[i] = ((WildcardType) type).getUpperBounds()[0]; @b@} @b@} @b@return types[types.length - 1]; @b@} @b@/** @b@ * This returns well known empty values for well-known java types. This returns null for types not @b@ * in the following list. @b@ * @b@ * <ul> @b@ * <li>{@code [Bb]oolean}</li> @b@ * <li>{@code byte[]}</li> @b@ * <li>{@code Collection}</li> @b@ * <li>{@code Iterator}</li> @b@ * <li>{@code List}</li> @b@ * <li>{@code Map}</li> @b@ * <li>{@code Set}</li> @b@ * </ul> @b@ * @b@ * <p/> When {@link Feign.Builder#decode404() decoding HTTP 404 status}, you'll need to teach @b@ * decoders a default empty value for a type. This method cheaply supports typical types by only @b@ * looking at the raw type (vs type hierarchy). Decorate for sophistication. @b@ */ @b@public static Object emptyValueOf(Type type) { @b@return EMPTIES.get(Types.getRawType(type)); @b@} @b@private static final Map<Class<?>, Object> EMPTIES; @b@static { @b@Map<Class<?>, Object> empties = new LinkedHashMap<Class<?>, Object>(); @b@empties.put(boolean.class, false); @b@empties.put(Boolean.class, false); @b@empties.put(byte[].class, new byte[0]); @b@empties.put(Collection.class, Collections.emptyList()); @b@empties.put(Iterator.class, new Iterator<Object>() { // Collections.emptyIterator is a 1.7 api @b@public boolean hasNext() { @b@return false; @b@} @b@public Object next() { @b@throw new NoSuchElementException(); @b@} @b@public void remove() { @b@throw new IllegalStateException(); @b@} @b@}); @b@empties.put(List.class, Collections.emptyList()); @b@empties.put(Map.class, Collections.emptyMap()); @b@empties.put(Set.class, Collections.emptySet()); @b@EMPTIES = Collections.unmodifiableMap(empties); @b@} @b@/** @b@ * Adapted from {@code com.google.common.io.CharStreams.toString()}. @b@ */ @b@public static String toString(Reader reader) throws IOException { @b@if (reader == null) { @b@return null; @b@} @b@try { @b@StringBuilder to = new StringBuilder(); @b@CharBuffer buf = CharBuffer.allocate(BUF_SIZE); @b@while (reader.read(buf) != -1) { @b@buf.flip(); @b@to.append(buf); @b@buf.clear(); @b@} @b@return to.toString(); @b@} finally { @b@ensureClosed(reader); @b@} @b@} @b@/** @b@ * Adapted from {@code com.google.common.io.ByteStreams.toByteArray()}. @b@ */ @b@public static byte[] toByteArray(InputStream in) throws IOException { @b@checkNotNull(in, "in"); @b@try { @b@ByteArrayOutputStream out = new ByteArrayOutputStream(); @b@copy(in, out); @b@return out.toByteArray(); @b@} finally { @b@ensureClosed(in); @b@} @b@} @b@/** @b@ * Adapted from {@code com.google.common.io.ByteStreams.copy()}. @b@ */ @b@private static long copy(InputStream from, OutputStream to) @b@throws IOException { @b@checkNotNull(from, "from"); @b@checkNotNull(to, "to"); @b@byte[] buf = new byte[BUF_SIZE]; @b@long total = 0; @b@while (true) { @b@int r = from.read(buf); @b@if (r == -1) { @b@break; @b@} @b@to.write(buf, 0, r); @b@total += r; @b@} @b@return total; @b@} @b@public static String decodeOrDefault(byte[] data, Charset charset, String defaultValue) { @b@if (data == null) { @b@return defaultValue; @b@} @b@checkNotNull(charset, "charset"); @b@try { @b@return charset.newDecoder().decode(ByteBuffer.wrap(data)).toString(); @b@} catch (CharacterCodingException ex) { @b@return defaultValue; @b@} @b@} @b@}