diff --git a/java.source.base/apichanges.xml b/java.source.base/apichanges.xml --- a/java.source.base/apichanges.xml +++ b/java.source.base/apichanges.xml @@ -49,6 +49,20 @@ Java Source API + + + Provide type suitable for declaration in source + + + + + + Support method which allows to infer a type suitable for use in symbol + declaration in the source was added. + + + + Allow to return also unimplemented default methods diff --git a/java.source.base/src/org/netbeans/api/java/source/TypeUtilities.java b/java.source.base/src/org/netbeans/api/java/source/TypeUtilities.java --- a/java.source.base/src/org/netbeans/api/java/source/TypeUtilities.java +++ b/java.source.base/src/org/netbeans/api/java/source/TypeUtilities.java @@ -43,8 +43,14 @@ */ package org.netbeans.api.java.source; +import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Type.CapturedType; +import com.sun.tools.javac.code.Type.ClassType; +import com.sun.tools.javac.code.Type.TypeVar; +import com.sun.tools.javac.code.TypeTag; import com.sun.tools.javac.code.Types; +import com.sun.tools.javac.model.JavacTypes; import java.util.Arrays; import java.util.EnumSet; import java.util.Iterator; @@ -163,6 +169,99 @@ return new TypeNameVisitor(opt.contains(TypeNameOptions.PRINT_AS_VARARG)).visit(type, opt.contains(TypeNameOptions.PRINT_FQN)).toString(); } + /** + * Returns a TypeMirror which can be represented in a source as a type declarator. The returned TypeMirror, + * if not erroneous, can be used as type of a variable or a method's return type. The method will attempt to + * infer proper wildcards or bounds. + *

+ * If the type could be represented in source, the method returns a type of {@link TypeKind#ERROR}. + * + * @param type the type to be polished + * @return the representable type or an error type + * @since 2.16 + */ + public TypeMirror getDenotableType(TypeMirror type) { + Types types = Types.instance(info.impl.getJavacTask().getContext()); + if (type == null) { + return types.createErrorType( + (Type)JavacTypes.instance(info.impl.getJavacTask().getContext()).getNoType(TypeKind.NONE) + ); + } + Type inType = (Type)type; + TypeKind tk = type.getKind(); + if (tk == TypeKind.ERROR) { + inType = (Type)info.getTrees().getOriginalType((ErrorType)type); + } else if (tk == TypeKind.NONE || tk == TypeKind.OTHER) { + return types.createErrorType(inType); + } + Type t = types.upward(inType, types.captures(inType)); + if (!t.isErroneous()) { + if (!checkDenotable(t)) { + return types.createErrorType(t); + } + } + if (t.hasTag(TypeTag.BOT)) { + return types.createErrorType(t); + } else { + return t; + } + } + + boolean checkDenotable(Type t) { + return denotableChecker.visit(t, null); + } + // where + + /** diamondTypeChecker: A type visitor that descends down the given type looking for non-denotable + * types. The visit methods return false as soon as a non-denotable type is encountered and true + * otherwise. + */ + private static final Types.SimpleVisitor denotableChecker = new Types.SimpleVisitor() { + @Override + public Boolean visitType(Type t, Void s) { + return true; + } + @Override + public Boolean visitClassType(ClassType t, Void s) { + if (t.isUnion() || t.isIntersection()) { + return false; + } + for (Type targ : t.allparams()) { + if (!visit(targ, s)) { + return false; + } + } + return true; + } + + @Override + public Boolean visitTypeVar(TypeVar t, Void s) { + /* Any type variable mentioned in the inferred type must have been declared as a type parameter + (i.e cannot have been produced by inference (18.4)) + */ + return (t.tsym.flags() & Flags.SYNTHETIC) == 0; + } + + @Override + public Boolean visitCapturedType(CapturedType t, Void s) { + /* Any type variable mentioned in the inferred type must have been declared as a type parameter + (i.e cannot have been produced by capture conversion (5.1.10)) + */ + return false; + } + + + @Override + public Boolean visitArrayType(Type.ArrayType t, Void s) { + return visit(t.elemtype, s); + } + + @Override + public Boolean visitWildcardType(Type.WildcardType t, Void s) { + return visit(t.type, s); + } + }; + /**Options for the {@link #getTypeName(javax.lang.model.type.TypeMirror, org.netbeans.api.java.source.TypeUtilities.TypeNameOptions[]) } method. * @since 0.62 */