@Override public Void visitIntersection(IntersectionType t, StringBuilderAndState<TypeMirror> state) { List<? extends TypeMirror> bounds = IgnoreCompletionFailures.in(t::getBounds); if (state.visitingMethod) { //the type erasure of an intersection type is the first type if (!bounds.isEmpty()) { bounds.get(0).accept(this, state); } } else { for (TypeMirror b : bounds) { b.accept(this, state); state.bld.append("+"); } } return null; }
@Test public void testGetUpperBoundMultipleBounds() throws IOException { compile("class Foo<T extends java.lang.CharSequence & java.lang.Runnable> { }"); TypeMirror charSequenceType = elements.getTypeElement("java.lang.CharSequence").asType(); TypeMirror runnableType = elements.getTypeElement("java.lang.Runnable").asType(); TypeVariable tVar = (TypeVariable) elements.getTypeElement("Foo").getTypeParameters().get(0).asType(); IntersectionType upperBound = (IntersectionType) tVar.getUpperBound(); List<? extends TypeMirror> bounds = upperBound.getBounds(); assertSame(2, bounds.size()); assertSameType(charSequenceType, bounds.get(0)); assertSameType(runnableType, bounds.get(1)); }
@Override public Void visitIntersection(IntersectionType t, StringBuilderAndState<TypeMirror> state) { Iterator<? extends TypeMirror> it = IgnoreCompletionFailures.in(t::getBounds).iterator(); if (it.hasNext()) { it.next().accept(this, state); } TypeVisitor<Void, StringBuilderAndState<TypeMirror>> me = this; it.forEachRemaining(b -> { state.bld.append(", "); b.accept(me, state); }); return null; }
@Override public AnnotatedIntersectionType getCopy(boolean copyAnnotations) { AnnotatedIntersectionType type = new AnnotatedIntersectionType((IntersectionType) actualType, atypeFactory); if (copyAnnotations) type.addAnnotations(annotations); type.supertypes = this.supertypes; return type; }
@Override public List<AnnotatedDeclaredType> directSuperTypes() { if (supertypes == null) { List<? extends TypeMirror> ubounds = ((IntersectionType)actualType).getBounds(); List<AnnotatedDeclaredType> res = new ArrayList<AnnotatedDeclaredType>(ubounds.size()); for (TypeMirror bnd : ubounds) { res.add((AnnotatedDeclaredType) createType(bnd, atypeFactory)); } supertypes = Collections.unmodifiableList(res); } return supertypes; }
@Override public <R, P> R accept(TypeVisitor<R, P> v, P p) { switch (kind) { case BOOLEAN: case BYTE: case SHORT: case INT: case LONG: case CHAR: case FLOAT: case DOUBLE: return v.visitPrimitive((PrimitiveType) this, p); case PACKAGE: case VOID: case NONE: return v.visitNoType((NoType) this, p); case NULL: return v.visitNull((NullType) this, p); case ARRAY: return v.visitArray((ArrayType) this, p); case DECLARED: return v.visitDeclared((DeclaredType) this, p); case ERROR: return v.visitError((ErrorType) this, p); case TYPEVAR: return v.visitTypeVariable((TypeVariable) this, p); case WILDCARD: return v.visitWildcard((WildcardType) this, p); case EXECUTABLE: return v.visitExecutable((ExecutableType) this, p); case OTHER: return v.visit(this, p); case UNION: return v.visitUnion((UnionType) this, p); case INTERSECTION: return v.visitIntersection((IntersectionType) this, p); default: throw new AssertionError(String.format("Unknown TypeKind: %s", kind)); } }
private boolean isSameIntersectionType(IntersectionType t1, IntersectionType t2) { List<? extends TypeMirror> t1Bounds = t1.getBounds(); List<? extends TypeMirror> t2Bounds = t2.getBounds(); if (t1Bounds.size() != t2Bounds.size()) { return false; } for (TypeMirror t1Bound : t1Bounds) { if (!listContainsType(t2Bounds, t1Bound)) { return false; } } return true; }
@Test public void testGetBounds() throws IOException { compile("class Foo<T extends java.lang.Runnable & java.lang.CharSequence> { }"); TypeMirror runnableType = elements.getTypeElement("java.lang.Runnable").asType(); TypeMirror charSequenceType = elements.getTypeElement("java.lang.CharSequence").asType(); IntersectionType intersectionType = (IntersectionType) getTypeParameterUpperBound("Foo", 0); List<? extends TypeMirror> bounds = intersectionType.getBounds(); assertSame(2, bounds.size()); assertSameType(runnableType, bounds.get(0)); assertSameType(charSequenceType, bounds.get(1)); }
@Test public void testIsSameTypeIntersectionType() throws IOException { compile( Joiner.on('\n') .join( "class Foo<T extends java.lang.CharSequence & java.lang.Runnable> { }", "class Bar<T extends java.lang.CharSequence & java.lang.Runnable> { }")); IntersectionType fooType = (IntersectionType) getTypeParameterUpperBound("Foo", 0); IntersectionType barType = (IntersectionType) getTypeParameterUpperBound("Bar", 0); assertSameType(fooType, barType); }
@Test public void testIsNotSameTypeIntersectionTypeDifferentSize() throws IOException { compile( Joiner.on('\n') .join( "class Foo<T extends java.lang.CharSequence & java.lang.Runnable> { }", "class Bar<T extends java.lang.CharSequence & java.lang.Runnable & java.io.Closeable> { }")); IntersectionType fooType = (IntersectionType) getTypeParameterUpperBound("Foo", 0); IntersectionType barType = (IntersectionType) getTypeParameterUpperBound("Bar", 0); assertNotSameType(fooType, barType); }
@Test public void testIsNotSameTypeIntersectionTypeDifferentSizeReversed() throws IOException { compile( Joiner.on('\n') .join( "class Foo<T extends java.lang.CharSequence & java.lang.Runnable & java.io.Closeable> { }", "class Bar<T extends java.lang.CharSequence & java.lang.Runnable> { }")); IntersectionType fooType = (IntersectionType) getTypeParameterUpperBound("Foo", 0); IntersectionType barType = (IntersectionType) getTypeParameterUpperBound("Bar", 0); assertNotSameType(fooType, barType); }
/** * We're not exactly sure why intersection types with the same bounds but in a different order are * considered the same type; after all, they can have different erasures. However, the javac * implementation behaves that way, so we must as well. * * <p>The relevant JLS8 sections are 4.4 and 4.9, if any future person wants to go see if they can * grok why this behavior is correct. */ @Test public void testIsSameTypeIntersectionTypeDifferentOrder() throws IOException { compile( Joiner.on('\n') .join( "class Foo<T extends java.lang.CharSequence & java.lang.Runnable> { }", "class Bar<T extends java.lang.Runnable & java.lang.CharSequence> { }")); IntersectionType fooType = (IntersectionType) getTypeParameterUpperBound("Foo", 0); IntersectionType barType = (IntersectionType) getTypeParameterUpperBound("Bar", 0); assertSameType(fooType, barType); }
@Test public void testIsNotSameTypeIntersectionTypeDifferentContents() throws IOException { compile( Joiner.on('\n') .join( "class Foo<T extends java.lang.CharSequence & java.lang.Runnable> { }", "class Bar<T extends java.lang.CharSequence & java.io.Closeable> { }")); IntersectionType fooType = (IntersectionType) getTypeParameterUpperBound("Foo", 0); IntersectionType barType = (IntersectionType) getTypeParameterUpperBound("Bar", 0); assertNotSameType(fooType, barType); }
@Override public Void visitIntersection(IntersectionType t, Void aVoid) { scans.add(t.toString()); return super.visitIntersection(t, aVoid); }
public static String fillGenerics(Map<String, String> types, List<? extends TypeMirror> params, String separator) { String result = ""; for (TypeMirror param : params) { if (result.length() > 0) { result += separator; } /** * "if" block's order is critically! E.g. IntersectionType is TypeVariable. */ if (param instanceof WildcardType) { result += "?"; final TypeMirror extendsBound = ((WildcardType) param).getExtendsBound(); if (extendsBound != null) { result += " extends " + fillGenerics(types, extendsBound); } final TypeMirror superBound = ((WildcardType) param).getSuperBound(); if (superBound != null) { result += " super " + fillGenerics(types, superBound); } } else if (param instanceof IntersectionType) { result += "?"; final List<? extends TypeMirror> bounds = ((IntersectionType) param).getBounds(); if (!bounds.isEmpty()) { result += " extends " + fillGenerics(types, bounds, " & "); } } else if (param instanceof DeclaredType) { result += ((DeclaredType) param).asElement(); final List<? extends TypeMirror> typeArguments = ((DeclaredType) param).getTypeArguments(); if (!typeArguments.isEmpty()) { final String s = fillGenerics(types, typeArguments); result += "<" + s + ">"; } } else if (param instanceof TypeVariable) { String type = types.get(param.toString()); if (type == null) { type = param.toString(); } result += type; } else { result += param; } } return result; }
@Override public Boolean visitIntersection(IntersectionType t, Void p) { return false; }
@Override public Boolean visitIntersection(IntersectionType t, Boolean p) { return false; }
@Override public R visitIntersection(IntersectionType t, P p) { scan(t.getBounds(), p); return super.visitIntersection(t, p); }
private void verifyTypeAcceptable(TypeMirror type, Set<String> acceptable) { if (type == null) return ; verifyAnnotations(type.getAnnotationMirrors(), acceptable); switch (type.getKind()) { case BOOLEAN: case BYTE: case CHAR: case DOUBLE: case FLOAT: case INT: case LONG: case SHORT: case VOID: case NONE: case NULL: return ; case DECLARED: DeclaredType dt = (DeclaredType) type; TypeElement outermostTypeElement = outermostTypeElement(dt.asElement()); String outermostType = outermostTypeElement.getQualifiedName().toString(); boolean isAcceptable = false; for (String acceptablePackage : acceptable) { if (outermostType.startsWith(acceptablePackage)) { isAcceptable = true; break; } } if (!isAcceptable) { error("Type not acceptable for this API: " + dt.toString()); } for (TypeMirror bound : dt.getTypeArguments()) { verifyTypeAcceptable(bound, acceptable); } break; case ARRAY: verifyTypeAcceptable(((ArrayType) type).getComponentType(), acceptable); break; case INTERSECTION: for (TypeMirror element : ((IntersectionType) type).getBounds()) { verifyTypeAcceptable(element, acceptable); } break; case TYPEVAR: verifyTypeAcceptable(((TypeVariable) type).getLowerBound(), acceptable); verifyTypeAcceptable(((TypeVariable) type).getUpperBound(), acceptable); break; case WILDCARD: verifyTypeAcceptable(((WildcardType) type).getExtendsBound(), acceptable); verifyTypeAcceptable(((WildcardType) type).getSuperBound(), acceptable); break; default: error("Type not acceptable for this API: " + type.toString()); break; } }
@Override protected Analysis createAnalysis(InvocationContext<TypeMirror> context) throws UnknownTypeException { TypeVariable typeVariable = (TypeVariable) context.field.refinedMirror(); final TypeMirror upperBound = typeVariable.getUpperBound(); CascadingTypeAnalyzer<?, ?, ?> transformation = null; if (upperBound instanceof DeclaredType) { // we have a concrete type, good resolvedMirror = upperBound; transformation = resolve(context.field.refine(resolvedMirror)); } else if (upperBound instanceof IntersectionType) { for (TypeMirror bound : ((IntersectionType) upperBound).getBounds()) { // as long as the first bound matches anything, we use it // TODO some bounds should have priority such as Parcelable and // serializable. but how do we decide? final CascadingTypeAnalyzer<?, ?, ?> found = resolve( context.field.refine(resolvedMirror)); if (found != null) { // we can probably do the following analysis to make this // better: // 1. Iterate through all strategies and save them as a list // 2. Further filter out the working ones (ones that does // not // throw) // 3. Do a super interface check on each of them and see // which // one is the most suitable resolvedMirror = bound; transformation = found; break; } } } if (resolvedMirror == null || transformation == null) throw new UnknownTypeException(context.field); return cascade(transformation.target(resolvedMirror).cast(TypeCastStrategy.NO_CAST), context, resolvedMirror); }
@Override public TypeElement visitIntersection(IntersectionType t, Void aVoid) { return t.getBounds().get(0).accept(this, null); }
@Override public final R visitIntersection(IntersectionType type, TypeMirror otherType) { return otherType instanceof IntersectionType ? visitIntersection(type, (IntersectionType) otherType) : unmatchedAction(type, otherType); }
protected R visitIntersection(IntersectionType type, IntersectionType otherType) { return defaultMatchAction(type, otherType); }
/** * Creates the appropriate AnnotatedTypeMirror specific wrapper for the * provided type * * @param type * @param atypeFactory * @return [to document] */ public static AnnotatedTypeMirror createType(TypeMirror type, AnnotatedTypeFactory atypeFactory) { if (type == null) { ErrorReporter.errorAbort("AnnotatedTypeMirror.createType: input type must not be null!"); return null; } com.sun.tools.javac.code.Type jctype = ((com.sun.tools.javac.code.Type)type); type = jctype.unannotatedType(); AnnotatedTypeMirror result; switch (type.getKind()) { case ARRAY: result = new AnnotatedArrayType((ArrayType) type, atypeFactory); break; case DECLARED: result = new AnnotatedDeclaredType((DeclaredType) type, atypeFactory); break; case ERROR: ErrorReporter.errorAbort("AnnotatedTypeMirror.createType: input should type-check already! Found error type: " + type); return null; // dead code case EXECUTABLE: result = new AnnotatedExecutableType((ExecutableType) type, atypeFactory); break; case VOID: case PACKAGE: case NONE: result = new AnnotatedNoType((NoType) type, atypeFactory); break; case NULL: result = new AnnotatedNullType((NullType) type, atypeFactory); break; case TYPEVAR: result = new AnnotatedTypeVariable((TypeVariable) type, atypeFactory); break; case WILDCARD: result = new AnnotatedWildcardType((WildcardType) type, atypeFactory); break; case INTERSECTION: result = new AnnotatedIntersectionType((IntersectionType) type, atypeFactory); break; case UNION: result = new AnnotatedUnionType((UnionType) type, atypeFactory); break; default: if (type.getKind().isPrimitive()) { result = new AnnotatedPrimitiveType((PrimitiveType) type, atypeFactory); break; } ErrorReporter.errorAbort("AnnotatedTypeMirror.createType: unidentified type " + type + " (" + type.getKind() + ")"); return null; // dead code } /*if (jctype.isAnnotated()) { result.addAnnotations(jctype.getAnnotationMirrors()); }*/ return result; }
@Override @Nullable public R visitIntersection(IntersectionType t, P p) { return scan(t.getBounds(), p); }
/** * This implementation visits an {@code IntersectionType} by calling * {@code defaultAction}. * * @param t {@inheritDoc} * @param p {@inheritDoc} * @return the result of {@code defaultAction} */ @Override public R visitIntersection(IntersectionType t, P p){ return defaultAction(t, p); }