001/*
002 * Copyright (C) 2014 Square, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.avaje.jsonb.core;
017
018import java.lang.annotation.Annotation;
019import java.lang.reflect.*;
020import java.util.*;
021
022/**
023 * Utility methods for defining Types.
024 */
025public final class Util {
026
027  static final Type[] EMPTY_TYPE_ARRAY = new Type[]{};
028
029  private Util() {
030  }
031
032  /**
033   * Returns an array type whose elements are all instances of {@code componentType}.
034   */
035  public static GenericArrayType arrayOf(Type elementType) {
036    return new Util.GenericArrayTypeImpl(elementType);
037  }
038
039  /**
040   * Returns a new parameterized type, applying {@code typeArguments} to {@code rawType}. Use this
041   * method if {@code rawType} is not enclosed in another type.
042   */
043  public static ParameterizedType newParameterizedType(Type rawType, Type... typeArguments) {
044    if (typeArguments.length == 0) {
045      throw new IllegalArgumentException("Missing type arguments for " + rawType);
046    }
047    return new Util.ParameterizedTypeImpl(null, rawType, typeArguments);
048  }
049
050  static boolean typesMatch(Type pattern, Type candidate) {
051    return Util.equals(pattern, candidate);
052  }
053
054  static boolean isAnnotationPresent(Set<? extends Annotation> annotations, Class<? extends Annotation> annotationClass) {
055    if (annotations.isEmpty()) return false; // Save an iterator in the common case.
056    for (Annotation annotation : annotations) {
057      if (annotation.annotationType() == annotationClass) return true;
058    }
059    return false;
060  }
061
062  static Type canonicalizeClass(Class<?> cls) {
063    return cls.isArray() ? new GenericArrayTypeImpl(canonicalize(cls.getComponentType())) : cls;
064  }
065
066  /**
067   * Returns a type that is functionally equal but not necessarily equal according to {@link
068   * Object#equals(Object) Object.equals()}.
069   */
070  static Type canonicalize(Type type) {
071    if (type instanceof Class) {
072      Class<?> c = (Class<?>) type;
073      return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
074
075    } else if (type instanceof ParameterizedType) {
076      if (type instanceof ParameterizedTypeImpl) return type;
077      ParameterizedType p = (ParameterizedType) type;
078      return new ParameterizedTypeImpl(
079        p.getOwnerType(), p.getRawType(), p.getActualTypeArguments());
080
081    } else if (type instanceof GenericArrayType) {
082      if (type instanceof GenericArrayTypeImpl) return type;
083      GenericArrayType g = (GenericArrayType) type;
084      return new GenericArrayTypeImpl(g.getGenericComponentType());
085
086    } else if (type instanceof WildcardType) {
087      if (type instanceof WildcardTypeImpl) return type;
088      WildcardType w = (WildcardType) type;
089      return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
090
091    } else {
092      return type; // This type is unsupported!
093    }
094  }
095
096  /**
097   * If type is a "? extends X" wildcard, returns X; otherwise returns type unchanged.
098   */
099  static Type removeSubtypeWildcard(Type type) {
100    if (!(type instanceof WildcardType)) return type;
101
102    Type[] lowerBounds = ((WildcardType) type).getLowerBounds();
103    if (lowerBounds.length != 0) return type;
104
105    Type[] upperBounds = ((WildcardType) type).getUpperBounds();
106    if (upperBounds.length != 1) throw new IllegalArgumentException();
107
108    return upperBounds[0];
109  }
110
111  static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
112    return resolve(context, contextRawType, toResolve, new LinkedHashSet<TypeVariable<?>>());
113  }
114
115  private static Type resolve(
116    Type context,
117    Class<?> contextRawType,
118    Type toResolve,
119    Collection<TypeVariable<?>> visitedTypeVariables) {
120    // This implementation is made a little more complicated in an attempt to avoid object-creation.
121    while (true) {
122      if (toResolve instanceof TypeVariable) {
123        TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
124        if (visitedTypeVariables.contains(typeVariable)) {
125          // cannot reduce due to infinite recursion
126          return toResolve;
127        } else {
128          visitedTypeVariables.add(typeVariable);
129        }
130        toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
131        if (toResolve == typeVariable) return toResolve;
132
133      } else if (toResolve instanceof Class && ((Class<?>) toResolve).isArray()) {
134        Class<?> original = (Class<?>) toResolve;
135        Type componentType = original.getComponentType();
136        Type newComponentType =
137          resolve(context, contextRawType, componentType, visitedTypeVariables);
138        return componentType == newComponentType ? original : arrayOf(newComponentType);
139
140      } else if (toResolve instanceof GenericArrayType) {
141        GenericArrayType original = (GenericArrayType) toResolve;
142        Type componentType = original.getGenericComponentType();
143        Type newComponentType =
144          resolve(context, contextRawType, componentType, visitedTypeVariables);
145        return componentType == newComponentType ? original : arrayOf(newComponentType);
146
147      } else if (toResolve instanceof ParameterizedType) {
148        ParameterizedType original = (ParameterizedType) toResolve;
149        Type ownerType = original.getOwnerType();
150        Type newOwnerType = resolve(context, contextRawType, ownerType, visitedTypeVariables);
151        boolean changed = newOwnerType != ownerType;
152
153        Type[] args = original.getActualTypeArguments();
154        for (int t = 0, length = args.length; t < length; t++) {
155          Type resolvedTypeArgument =
156            resolve(context, contextRawType, args[t], visitedTypeVariables);
157          if (resolvedTypeArgument != args[t]) {
158            if (!changed) {
159              args = args.clone();
160              changed = true;
161            }
162            args[t] = resolvedTypeArgument;
163          }
164        }
165
166        return changed
167          ? new ParameterizedTypeImpl(newOwnerType, original.getRawType(), args)
168          : original;
169
170      } else if (toResolve instanceof WildcardType) {
171        WildcardType original = (WildcardType) toResolve;
172        Type[] originalLowerBound = original.getLowerBounds();
173        Type[] originalUpperBound = original.getUpperBounds();
174
175        if (originalLowerBound.length == 1) {
176          Type lowerBound =
177            resolve(context, contextRawType, originalLowerBound[0], visitedTypeVariables);
178          if (lowerBound != originalLowerBound[0]) {
179            return supertypeOf(lowerBound);
180          }
181        } else if (originalUpperBound.length == 1) {
182          Type upperBound =
183            resolve(context, contextRawType, originalUpperBound[0], visitedTypeVariables);
184          if (upperBound != originalUpperBound[0]) {
185            return subtypeOf(upperBound);
186          }
187        }
188        return original;
189
190      } else {
191        return toResolve;
192      }
193    }
194  }
195
196  static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) {
197    Class<?> declaredByRaw = declaringClassOf(unknown);
198
199    // We can't reduce this further.
200    if (declaredByRaw == null) return unknown;
201
202    Type declaredBy = genericSupertype(context, contextRawType, declaredByRaw);
203    if (declaredBy instanceof ParameterizedType) {
204      int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
205      return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
206    }
207
208    return unknown;
209  }
210
211  /**
212   * Returns the generic supertype for {@code supertype}. For example, given a class {@code
213   * IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the
214   * result when the supertype is {@code Collection.class} is {@code Collection<Integer>}.
215   */
216  static Type genericSupertype(Type context, Class<?> rawType, Class<?> toResolve) {
217    if (toResolve == rawType) {
218      return context;
219    }
220
221    // we skip searching through interfaces if unknown is an interface
222    if (toResolve.isInterface()) {
223      Class<?>[] interfaces = rawType.getInterfaces();
224      for (int i = 0, length = interfaces.length; i < length; i++) {
225        if (interfaces[i] == toResolve) {
226          return rawType.getGenericInterfaces()[i];
227        } else if (toResolve.isAssignableFrom(interfaces[i])) {
228          return genericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
229        }
230      }
231    }
232
233    // check our supertypes
234    if (!rawType.isInterface()) {
235      while (rawType != Object.class) {
236        Class<?> rawSupertype = rawType.getSuperclass();
237        if (rawSupertype == toResolve) {
238          return rawType.getGenericSuperclass();
239        } else if (toResolve.isAssignableFrom(rawSupertype)) {
240          return genericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
241        }
242        rawType = rawSupertype;
243      }
244    }
245
246    // we can't resolve this further
247    return toResolve;
248  }
249
250  static int hashCodeOrZero(Object o) {
251    return o != null ? o.hashCode() : 0;
252  }
253
254  static String typeToString(Type type) {
255    return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
256  }
257
258  static int indexOf(Object[] array, Object toFind) {
259    for (int i = 0; i < array.length; i++) {
260      if (toFind.equals(array[i])) return i;
261    }
262    throw new NoSuchElementException();
263  }
264
265  /**
266   * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
267   * a class.
268   */
269  static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
270    GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
271    return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null;
272  }
273
274  static void checkNotPrimitive(Type type) {
275    if ((type instanceof Class<?>) && ((Class<?>) type).isPrimitive()) {
276      throw new IllegalArgumentException("Unexpected primitive " + type + ". Use the boxed type.");
277    }
278  }
279
280  static final class ParameterizedTypeImpl implements ParameterizedType {
281    private final Type ownerType;
282    private final Type rawType;
283    public final Type[] typeArguments;
284
285    public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
286      // Require an owner type if the raw type needs it.
287      if (rawType instanceof Class<?>) {
288        Class<?> enclosingClass = ((Class<?>) rawType).getEnclosingClass();
289        if (ownerType != null) {
290          if (enclosingClass == null || Util.rawType(ownerType) != enclosingClass) {
291            throw new IllegalArgumentException(
292              "unexpected owner type for " + rawType + ": " + ownerType);
293          }
294        } else if (enclosingClass != null) {
295          throw new IllegalArgumentException("unexpected owner type for " + rawType + ": null");
296        }
297      }
298
299      this.ownerType = ownerType == null ? null : canonicalize(ownerType);
300      this.rawType = canonicalize(rawType);
301      this.typeArguments = typeArguments.clone();
302      for (int t = 0; t < this.typeArguments.length; t++) {
303        if (this.typeArguments[t] == null) throw new NullPointerException();
304        checkNotPrimitive(this.typeArguments[t]);
305        this.typeArguments[t] = canonicalize(this.typeArguments[t]);
306      }
307    }
308
309    @Override
310    public Type[] getActualTypeArguments() {
311      return typeArguments.clone();
312    }
313
314    @Override
315    public Type getRawType() {
316      return rawType;
317    }
318
319    @Override
320    public Type getOwnerType() {
321      return ownerType;
322    }
323
324    @Override
325    public boolean equals(Object other) {
326      return other instanceof ParameterizedType && Util.equals(this, (ParameterizedType) other);
327    }
328
329    @Override
330    public int hashCode() {
331      return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType);
332    }
333
334    @Override
335    public String toString() {
336      StringBuilder result = new StringBuilder(30 * (typeArguments.length + 1));
337      result.append(typeToString(rawType));
338
339      if (typeArguments.length == 0) {
340        return result.toString();
341      }
342
343      result.append("<").append(typeToString(typeArguments[0]));
344      for (int i = 1; i < typeArguments.length; i++) {
345        result.append(", ").append(typeToString(typeArguments[i]));
346      }
347      return result.append(">").toString();
348    }
349  }
350
351  static final class GenericArrayTypeImpl implements GenericArrayType {
352    private final Type componentType;
353
354    GenericArrayTypeImpl(Type componentType) {
355      this.componentType = canonicalize(componentType);
356    }
357
358    @Override
359    public Type getGenericComponentType() {
360      return componentType;
361    }
362
363    @Override
364    public boolean equals(Object o) {
365      return o instanceof GenericArrayType && Util.equals(this, (GenericArrayType) o);
366    }
367
368    @Override
369    public int hashCode() {
370      return componentType.hashCode();
371    }
372
373    @Override
374    public String toString() {
375      return typeToString(componentType) + "[]";
376    }
377  }
378
379  /**
380   * The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only
381   * support what the Java 6 language needs - at most one bound. If a lower bound is set, the upper
382   * bound must be Object.class.
383   */
384  static final class WildcardTypeImpl implements WildcardType {
385    private final Type upperBound;
386    private final Type lowerBound;
387
388    WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
389      if (lowerBounds.length > 1) throw new IllegalArgumentException();
390      if (upperBounds.length != 1) throw new IllegalArgumentException();
391
392      if (lowerBounds.length == 1) {
393        if (lowerBounds[0] == null) throw new NullPointerException();
394        checkNotPrimitive(lowerBounds[0]);
395        if (upperBounds[0] != Object.class) throw new IllegalArgumentException();
396        this.lowerBound = canonicalize(lowerBounds[0]);
397        this.upperBound = Object.class;
398
399      } else {
400        if (upperBounds[0] == null) throw new NullPointerException();
401        checkNotPrimitive(upperBounds[0]);
402        this.lowerBound = null;
403        this.upperBound = canonicalize(upperBounds[0]);
404      }
405    }
406
407    @Override
408    public Type[] getUpperBounds() {
409      return new Type[]{upperBound};
410    }
411
412    @Override
413    public Type[] getLowerBounds() {
414      return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY;
415    }
416
417    @Override
418    public boolean equals(Object other) {
419      return other instanceof WildcardType && Util.equals(this, (WildcardType) other);
420    }
421
422    @Override
423    public int hashCode() {
424      // This equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()).
425      return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode());
426    }
427
428    @Override
429    public String toString() {
430      if (lowerBound != null) {
431        return "? super " + typeToString(lowerBound);
432      } else if (upperBound == Object.class) {
433        return "?";
434      } else {
435        return "? extends " + typeToString(upperBound);
436      }
437    }
438  }
439
440  static String typeAnnotatedWithAnnotations(Type type, Set<? extends Annotation> annotations) {
441    return type + (annotations.isEmpty() ? " (with no annotations)" : " annotated " + annotations);
442  }
443
444  /**
445   * Returns a type that represents an unknown type that extends {@code bound}. For example, if
446   * {@code bound} is {@code CharSequence.class}, this returns {@code ? extends CharSequence}. If
447   * {@code bound} is {@code Object.class}, this returns {@code ?}, which is shorthand for {@code ?
448   * extends Object}.
449   */
450  static WildcardType subtypeOf(Type bound) {
451    Type[] upperBounds;
452    if (bound instanceof WildcardType) {
453      upperBounds = ((WildcardType) bound).getUpperBounds();
454    } else {
455      upperBounds = new Type[]{bound};
456    }
457    return new Util.WildcardTypeImpl(upperBounds, EMPTY_TYPE_ARRAY);
458  }
459
460  /**
461   * Returns a type that represents an unknown supertype of {@code bound}. For example, if {@code
462   * bound} is {@code String.class}, this returns {@code ? super String}.
463   */
464  static WildcardType supertypeOf(Type bound) {
465    Type[] lowerBounds;
466    if (bound instanceof WildcardType) {
467      lowerBounds = ((WildcardType) bound).getLowerBounds();
468    } else {
469      lowerBounds = new Type[]{bound};
470    }
471    return new Util.WildcardTypeImpl(new Type[]{Object.class}, lowerBounds);
472  }
473
474
475  static Class<?> rawType(Type type) {
476    if (type instanceof Class<?>) {
477      // type is a normal class.
478      return (Class<?>) type;
479
480    } else if (type instanceof ParameterizedType) {
481      ParameterizedType parameterizedType = (ParameterizedType) type;
482
483      // I'm not exactly sure why getRawType() returns Type instead of Class. Neal isn't either but
484      // suspects some pathological case related to nested classes exists.
485      Type rawType = parameterizedType.getRawType();
486      return (Class<?>) rawType;
487
488    } else if (type instanceof GenericArrayType) {
489      Type componentType = ((GenericArrayType) type).getGenericComponentType();
490      return Array.newInstance(rawType(componentType), 0).getClass();
491
492    } else if (type instanceof TypeVariable) {
493      // We could use the variable's bounds, but that won't work if there are multiple. having a raw
494      // type that's more general than necessary is okay.
495      return Object.class;
496
497    } else if (type instanceof WildcardType) {
498      return rawType(((WildcardType) type).getUpperBounds()[0]);
499
500    } else {
501      String className = type == null ? "null" : type.getClass().getName();
502      throw new IllegalArgumentException(
503        "Expected a Class, ParameterizedType, or "
504          + "GenericArrayType, but <"
505          + type
506          + "> is of type "
507          + className);
508    }
509  }
510
511  /**
512   * Returns the element type of this collection type.
513   *
514   * @throws IllegalArgumentException if this type is not a collection.
515   */
516  static Type collectionElementType(Type context) {
517    Type collectionType = supertype(context, Collection.class, Collection.class);
518    if (collectionType instanceof WildcardType) {
519      collectionType = ((WildcardType) collectionType).getUpperBounds()[0];
520    }
521    if (collectionType instanceof ParameterizedType) {
522      return ((ParameterizedType) collectionType).getActualTypeArguments()[0];
523    }
524    return Object.class;
525  }
526
527  /**
528   * Returns true if {@code a} and {@code b} are equal.
529   */
530  static boolean equals(Type a, Type b) {
531    if (a == b) {
532      return true; // Also handles (a == null && b == null).
533
534    } else if (a instanceof Class) {
535      if (b instanceof GenericArrayType) {
536        return equals(((Class<?>) a).getComponentType(), ((GenericArrayType) b).getGenericComponentType());
537      }
538      return a.equals(b); // Class already specifies equals().
539
540    } else if (a instanceof ParameterizedType) {
541      if (!(b instanceof ParameterizedType)) return false;
542      ParameterizedType pa = (ParameterizedType) a;
543      ParameterizedType pb = (ParameterizedType) b;
544      Type[] aTypeArguments =
545        pa instanceof Util.ParameterizedTypeImpl
546          ? ((Util.ParameterizedTypeImpl) pa).typeArguments
547          : pa.getActualTypeArguments();
548      Type[] bTypeArguments =
549        pb instanceof Util.ParameterizedTypeImpl
550          ? ((Util.ParameterizedTypeImpl) pb).typeArguments
551          : pb.getActualTypeArguments();
552      return equals(pa.getOwnerType(), pb.getOwnerType())
553        && pa.getRawType().equals(pb.getRawType())
554        && Arrays.equals(aTypeArguments, bTypeArguments);
555
556    } else if (a instanceof GenericArrayType) {
557      if (b instanceof Class) {
558        return equals(((Class<?>) b).getComponentType(), ((GenericArrayType) a).getGenericComponentType());
559      }
560      if (!(b instanceof GenericArrayType)) return false;
561      GenericArrayType ga = (GenericArrayType) a;
562      GenericArrayType gb = (GenericArrayType) b;
563      return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
564
565    } else if (a instanceof WildcardType) {
566      if (!(b instanceof WildcardType)) return false;
567      WildcardType wa = (WildcardType) a;
568      WildcardType wb = (WildcardType) b;
569      return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds())
570        && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds());
571
572    } else if (a instanceof TypeVariable) {
573      if (!(b instanceof TypeVariable)) return false;
574      TypeVariable<?> va = (TypeVariable<?>) a;
575      TypeVariable<?> vb = (TypeVariable<?>) b;
576      return va.getGenericDeclaration() == vb.getGenericDeclaration()
577        && va.getName().equals(vb.getName());
578
579    } else {
580      // This isn't a supported type.
581      return false;
582    }
583  }
584
585  /**
586   * Returns a two element array containing this map's key and value types in positions 0 and 1
587   * respectively.
588   */
589  static Type mapValueType(Type context, Class<?> contextRawType) {
590    // Work around a problem with the declaration of java.util.Properties. That class should extend
591    // Hashtable<String, String>, but it's declared to extend Hashtable<Object, Object>.
592    if (context == Properties.class) {
593      return String.class;
594    }
595    Type mapType = supertype(context, contextRawType, Map.class);
596    if (mapType instanceof ParameterizedType) {
597      ParameterizedType mapParameterizedType = (ParameterizedType) mapType;
598      return mapParameterizedType.getActualTypeArguments()[1];
599    }
600    return Object.class;
601  }
602
603  /**
604   * Returns the generic form of {@code supertype}. For example, if this is {@code
605   * ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code
606   * Iterable.class}.
607   *
608   * @param supertype a superclass of, or interface implemented by, this.
609   */
610  static Type supertype(Type context, Class<?> contextRawType, Class<?> supertype) {
611    if (!supertype.isAssignableFrom(contextRawType)) throw new IllegalArgumentException();
612    return resolve(context, contextRawType, genericSupertype(context, contextRawType, supertype));
613  }
614
615  /**
616   * Returns the element type of {@code type} if it is an array type, or null if it is not an array
617   * type.
618   */
619  static Type arrayComponentType(Type type) {
620    if (type instanceof GenericArrayType) {
621      return ((GenericArrayType) type).getGenericComponentType();
622    } else if (type instanceof Class) {
623      return ((Class<?>) type).getComponentType();
624    } else {
625      return null;
626    }
627  }
628}