|
63 | 63 | import java.util.HashMap; |
64 | 64 | import java.util.HashSet; |
65 | 65 | import java.util.LinkedHashSet; |
| 66 | +import java.util.LinkedList; |
66 | 67 | import java.util.List; |
67 | 68 | import java.util.Map; |
68 | 69 | import java.util.Objects; |
69 | 70 | import java.util.Optional; |
| 71 | +import java.util.Queue; |
70 | 72 | import java.util.Set; |
71 | 73 | import java.util.stream.IntStream; |
72 | 74 |
|
@@ -741,6 +743,167 @@ public static Type param(final Type type, final Class<?> c, final int no) { |
741 | 743 | c.getTypeParameters()[no]); |
742 | 744 | } |
743 | 745 |
|
| 746 | + /** |
| 747 | + * Determines the greatest common supertype of all types in the input array |
| 748 | + * |
| 749 | + * @param types |
| 750 | + * The array of subtypes, for which the supertype is found. |
| 751 | + * @param wildcardSingleIface |
| 752 | + * The default behavior, when the method finds multiple suitable |
| 753 | + * interfaces, is to create a wildcard with all suitable interfaces |
| 754 | + * as upper bounds. If this {@code boolean} is set to true, a |
| 755 | + * wildcard will also be created if only one suitable interface is |
| 756 | + * found. If false, the return will be the interface itself (i.e. not |
| 757 | + * a wildcard). |
| 758 | + * @return a {@link Type} that is a supertype of all {@link Type}s in the types array. |
| 759 | + */ |
| 760 | + public static Type greatestCommonSuperType(final Type[] types, final boolean wildcardSingleIface) { |
| 761 | + |
| 762 | + if (types.length == 0) |
| 763 | + return null; |
| 764 | + // make sure that all types are supported |
| 765 | + // TODO: are there any other types that aren't fully supported? |
| 766 | + for (Type t : types) { |
| 767 | + if (t instanceof TypeVariable<?>) |
| 768 | + throw new UnsupportedOperationException("Unsupported type: " + t); |
| 769 | + } |
| 770 | + |
| 771 | + // We can effectively find the greatest common super type by assuming that |
| 772 | + // either: |
| 773 | + // 1) superType extends a superclass of all types in types (this check is |
| 774 | + // symmetric) |
| 775 | + // 2) superType implements an interface implemented by all types in typws (this |
| 776 | + // check is symmetric) |
| 777 | + |
| 778 | + Type superType = types[0]; |
| 779 | + |
| 780 | + // 1) |
| 781 | + // prefer any non-Object superClass over implemented interfaces. |
| 782 | + while (Types.raw(superType) != Object.class) { |
| 783 | + // check assignability of superclass with type params |
| 784 | + Type pType = Types.getExactSuperType(types[0], Types.raw(superType)); |
| 785 | + if (Types.isAssignable(types, pType) == -1) |
| 786 | + return superType; |
| 787 | + |
| 788 | + // if we have a parameterizedtype whose rawtype is assignable to all of types, |
| 789 | + // we just have to resolve each of pType's type variables. |
| 790 | + if (pType instanceof ParameterizedType && // |
| 791 | + Types.isAssignable(types, Types.raw(superType)) == -1) { |
| 792 | + ParameterizedType[] castedTypes = new ParameterizedType[types.length]; |
| 793 | + castedTypes[0] = (ParameterizedType) pType; |
| 794 | + // generate parameterizedTypes of each type in types w.r.t. supertype |
| 795 | + for (int i = 1; i < castedTypes.length; i++) { |
| 796 | + Type t = Types.getExactSuperType(types[i], Types.raw(superType)); |
| 797 | + if (!(t instanceof ParameterizedType)) |
| 798 | + continue; |
| 799 | + castedTypes[i] = (ParameterizedType) t; |
| 800 | + } |
| 801 | + // resolve each of the i type variables of superType using |
| 802 | + // greatestCommonSupertype |
| 803 | + Type[] resolvedTypeArgs = new Type[castedTypes[0].getActualTypeArguments().length]; |
| 804 | + for (int i = 0; i < resolvedTypeArgs.length; i++) { |
| 805 | + Type[] typeVarsI = new Type[types.length]; |
| 806 | + for (int j = 0; j < typeVarsI.length; j++) { |
| 807 | + typeVarsI[j] = castedTypes[j].getActualTypeArguments()[i]; |
| 808 | + } |
| 809 | + resolvedTypeArgs[i] = wildcard(new Type[] { greatestCommonSuperType(typeVarsI, true) }, |
| 810 | + new Type[] {}); |
| 811 | + } |
| 812 | + |
| 813 | + // return supertype parameterized with the resolved type args |
| 814 | + return Types.parameterize(Types.raw(superType), resolvedTypeArgs); |
| 815 | + } |
| 816 | + if (Types.raw(superType).isInterface()) |
| 817 | + break; |
| 818 | + superType = Types.getExactSuperType(superType, Types.raw(superType).getSuperclass()); |
| 819 | + } |
| 820 | + |
| 821 | + // 2) |
| 822 | + List<Type> sharedInterfaces = new ArrayList<>(); |
| 823 | + Queue<Type> superInterfaces = new LinkedList<>(); |
| 824 | + if (Types.raw(types[0]).isInterface()) |
| 825 | + superInterfaces.add(types[0]); |
| 826 | + else |
| 827 | + for (Type superT1 : Types.raw(types[0]).getGenericInterfaces()) |
| 828 | + superInterfaces.add(superT1); |
| 829 | + while (superInterfaces.size() > 0) { |
| 830 | + Type type = superInterfaces.remove(); |
| 831 | + Type pType = Types.getExactSuperType(types[0], Types.raw(type)); |
| 832 | + if (Types.isAssignable(types, pType) == -1) { |
| 833 | + sharedInterfaces.add(pType); |
| 834 | + continue; |
| 835 | + } |
| 836 | + // if we have a parameterizedtype whose rawtype is assignable to all of types, |
| 837 | + // we just have to resolve each of pType's type variables. |
| 838 | + if (pType instanceof ParameterizedType && // |
| 839 | + Types.isAssignable(types, Types.raw(pType)) == -1) { |
| 840 | + ParameterizedType[] castedTypes = new ParameterizedType[types.length]; |
| 841 | + castedTypes[0] = (ParameterizedType) pType; |
| 842 | + // generate parameterizedTypes of each type in types w.r.t. supertype |
| 843 | + for (int i = 1; i < castedTypes.length; i++) { |
| 844 | + Type t = Types.getExactSuperType(types[i], Types.raw(pType)); |
| 845 | + if (!(t instanceof ParameterizedType)) |
| 846 | + continue; |
| 847 | + castedTypes[i] = (ParameterizedType) t; |
| 848 | + } |
| 849 | + // resolve each of the i type variables of pType using greatestCommonSupertype |
| 850 | + Type[] resolvedTypeArgs = new Type[castedTypes[0].getActualTypeArguments().length]; |
| 851 | + for (int i = 0; i < resolvedTypeArgs.length; i++) { |
| 852 | + Type[] typeVarsI = new Type[types.length]; |
| 853 | + for (int j = 0; j < typeVarsI.length; j++) { |
| 854 | + typeVarsI[j] = castedTypes[j].getActualTypeArguments()[i]; |
| 855 | + } |
| 856 | + // If each of these types implements some recursive interface, e.g. Comparable, |
| 857 | + // the best we can do is return an unbounded wildcard. |
| 858 | + if (Arrays.equals(types, typeVarsI)) |
| 859 | + resolvedTypeArgs[i] = wildcard(); |
| 860 | + else |
| 861 | + resolvedTypeArgs[i] = greatestCommonSuperType(typeVarsI, true); |
| 862 | + } |
| 863 | + |
| 864 | + // return supertype parameterized with the resolved type args |
| 865 | + return Types.parameterize(Types.raw(pType), resolvedTypeArgs); |
| 866 | + } |
| 867 | + |
| 868 | + // if this interface is not a supertype of all of types, maybe one of its |
| 869 | + // inherited interfaces is. N.B. we don't want to keep searching through the |
| 870 | + // interface hierarchy, however, if we have have found a type that satisfies all |
| 871 | + // of types. Thus we stop adding to the list if we have found at least one |
| 872 | + // satisfying interface. Do note, however, that this does not prevent multiple |
| 873 | + // satisfying interfaces at the same depth from being found. |
| 874 | + if (sharedInterfaces.size() == 0) |
| 875 | + for (Type superT1 : Types.raw(type).getGenericInterfaces()) |
| 876 | + superInterfaces.add(superT1); |
| 877 | + } |
| 878 | + if (sharedInterfaces.size() == 1 && !wildcardSingleIface) { |
| 879 | + return sharedInterfaces.get(0); |
| 880 | + } else if (sharedInterfaces.size() > 0) { |
| 881 | + return wildcard(sharedInterfaces.toArray(new Type[] {}), new Type[] {}); |
| 882 | + } |
| 883 | + return Object.class; |
| 884 | + } |
| 885 | + |
| 886 | + /** |
| 887 | + * Discerns whether it would be legal to assign a group of references of types |
| 888 | + * {@code source} to a reference of type {@code target}. |
| 889 | + * |
| 890 | + * @param source |
| 891 | + * The types from which assignment is desired. |
| 892 | + * @param target |
| 893 | + * The type to which assignment is desired. |
| 894 | + * @return the index of the first type not assignable to {@code target}. If all |
| 895 | + * types are assignable, returns {@code -1} |
| 896 | + * @throws NullPointerException |
| 897 | + * if {@code target} is null. |
| 898 | + * @see Class#isAssignableFrom(Class) |
| 899 | + */ |
| 900 | + private static int isAssignable(final Type[] sources, final Type target) { |
| 901 | + for(int i = 0; i < sources.length; i++) { |
| 902 | + if(!Types.isAssignable(sources[i], target)) return i; |
| 903 | + } |
| 904 | + return -1; |
| 905 | + } |
| 906 | + |
744 | 907 | /** |
745 | 908 | * Discerns whether it would be legal to assign a reference of type |
746 | 909 | * {@code source} to a reference of type {@code target}. |
|
0 commit comments