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