--- old/src/share/classes/java/lang/Class.java 2013-04-03 00:21:04.000000000 -0700 +++ new/src/share/classes/java/lang/Class.java 2013-04-03 00:21:04.000000000 -0700 @@ -113,20 +113,19 @@ * @see java.lang.ClassLoader#defineClass(byte[], int, int) * @since JDK1.0 */ -public final - class Class implements java.io.Serializable, +public final class Class implements java.io.Serializable, java.lang.reflect.GenericDeclaration, java.lang.reflect.Type, java.lang.reflect.AnnotatedElement { private static final int ANNOTATION= 0x00002000; private static final int ENUM = 0x00004000; private static final int SYNTHETIC = 0x00001000; - + private static native void registerNatives(); static { registerNatives(); } - + /* * Constructor. Only the Java Virtual Machine creates Class * objects. @@ -150,6 +149,75 @@ + getName(); } + /** + * Returns a string describing this {@code Class}, including + * information about modifiers and type parameters. + * + * The string is formatted as list of type modifiers, if any, + * followed by the kind of type (empty string for primitive types + * and {@code class}, {@code enum}, {@code interface}, or {@code + * @interface}, as appropriate), followed by the type's name, + * followed by an angle-bracketed comma-separated list of the + * type's type parameters, if any. + * + * A space is used to separate modifiers from one another and to + * separate any modifiers from the kind of type. The modifiers + * occur in canonical order. If there are no type parameters, the + * type parameter list is elided. + * + *

Note that since information about the runtime representation + * of a type is being generated, modifiers not present on the + * originating source code or illegal on the originating source + * code may be present. + * + * @return a string describing this {@code Class}, including + * information about modifiers and type parameters + * + * @since 1.8 + */ + public String toGenericString() { + if (isPrimitive()) { + return toString(); + } else { + StringBuffer sb = new StringBuffer(); + + // Class modifiers are a superset of interface modifiers + int modifiers = getModifiers() & Modifier.classModifiers(); + if (modifiers != 0) { + sb.append(Modifier.toString(modifiers)); + sb.append(' '); + } + + if (isAnnotation()) { + sb.append('@'); + } + if (isInterface()) { // Note: all annotation types are interfaces + sb.append("interface"); + } else { + if (isEnum()) + sb.append("enum"); + else + sb.append("class"); + } + sb.append(' '); + sb.append(getName()); + + TypeVariable[] typeparms = getTypeParameters(); + if (typeparms.length > 0) { + boolean first = true; + sb.append('<'); + for(TypeVariable typeparm: typeparms) { + if (!first) + sb.append(','); + sb.append(typeparm.getTypeName()); + first = false; + } + sb.append('>'); + } + + return sb.toString(); + } + } /** * Returns the {@code Class} object associated with the class or @@ -1164,6 +1232,32 @@ } /** + * Return an informative string for the name of this type. + * + * @return an informative string for the name of this type + * @since 1.8 + */ + public String getTypeName() { + if (isArray()) { + try { + Class cl = this; + int dimensions = 0; + while (cl.isArray()) { + dimensions++; + cl = cl.getComponentType(); + } + StringBuilder sb = new StringBuilder(); + sb.append(cl.getName()); + for (int i = 0; i < dimensions; i++) { + sb.append("[]"); + } + return sb.toString(); + } catch (Throwable e) { /*FALLTHRU*/ } + } + return getName(); + } + + /** * Character.isDigit answers {@code true} to some non-ascii * digits. This one does not. */ --- old/src/share/classes/java/lang/reflect/Constructor.java 2013-04-03 00:21:05.000000000 -0700 +++ new/src/share/classes/java/lang/reflect/Constructor.java 2013-04-03 00:21:05.000000000 -0700 @@ -297,7 +297,7 @@ @Override void specificToStringHeader(StringBuilder sb) { - sb.append(Field.getTypeName(getDeclaringClass())); + sb.append(getDeclaringClass().getTypeName()); } /** --- old/src/share/classes/java/lang/reflect/Executable.java 2013-04-03 00:21:05.000000000 -0700 +++ new/src/share/classes/java/lang/reflect/Executable.java 2013-04-03 00:21:05.000000000 -0700 @@ -82,7 +82,7 @@ void separateWithCommas(Class[] types, StringBuilder sb) { for (int j = 0; j < types.length; j++) { - sb.append(Field.getTypeName(types[j])); + sb.append(types[j].getTypeName()); if (j < (types.length - 1)) sb.append(","); } @@ -161,9 +161,7 @@ sb.append('('); Type[] params = getGenericParameterTypes(); for (int j = 0; j < params.length; j++) { - String param = (params[j] instanceof Class)? - Field.getTypeName((Class)params[j]): - (params[j].toString()); + String param = params[j].getTypeName(); if (isVarArgs() && (j == params.length - 1)) // replace T[] with T... param = param.replaceFirst("\\[\\]$", "..."); sb.append(param); --- old/src/share/classes/java/lang/reflect/Field.java 2013-04-03 00:21:06.000000000 -0700 +++ new/src/share/classes/java/lang/reflect/Field.java 2013-04-03 00:21:06.000000000 -0700 @@ -295,8 +295,8 @@ public String toString() { int mod = getModifiers(); return (((mod == 0) ? "" : (Modifier.toString(mod) + " ")) - + getTypeName(getType()) + " " - + getTypeName(getDeclaringClass()) + "." + + getType().getTypeName() + " " + + getDeclaringClass().getTypeName() + "." + getName()); } @@ -324,9 +324,8 @@ int mod = getModifiers(); Type fieldType = getGenericType(); return (((mod == 0) ? "" : (Modifier.toString(mod) + " ")) - + ((fieldType instanceof Class) ? - getTypeName((Class)fieldType): fieldType.toString())+ " " - + getTypeName(getDeclaringClass()) + "." + + fieldType.getTypeName() + " " + + getDeclaringClass().getTypeName() + "." + getName()); } @@ -996,29 +995,6 @@ } } - /* - * Utility routine to paper over array type names - */ - static String getTypeName(Class type) { - if (type.isArray()) { - try { - Class cl = type; - int dimensions = 0; - while (cl.isArray()) { - dimensions++; - cl = cl.getComponentType(); - } - StringBuffer sb = new StringBuffer(); - sb.append(cl.getName()); - for (int i = 0; i < dimensions; i++) { - sb.append("[]"); - } - return sb.toString(); - } catch (Throwable e) { /*FALLTHRU*/ } - } - return type.getName(); - } - /** * @throws NullPointerException {@inheritDoc} * @since 1.5 --- old/src/share/classes/java/lang/reflect/Method.java 2013-04-03 00:21:06.000000000 -0700 +++ new/src/share/classes/java/lang/reflect/Method.java 2013-04-03 00:21:06.000000000 -0700 @@ -342,9 +342,8 @@ * specified by "The Java Language Specification". This is * {@code public}, {@code protected} or {@code private} first, * and then other modifiers in the following order: - * {@code abstract}, {@code static}, {@code final}, - * {@code synchronized}, {@code native}, {@code strictfp}, - * {@code default}. + * {@code abstract}, {@code default}, {@code static}, {@code final}, + * {@code synchronized}, {@code native}, {@code strictfp}. * * @return a string describing this {@code Method} * @@ -359,8 +358,8 @@ @Override void specificToStringHeader(StringBuilder sb) { - sb.append(Field.getTypeName(getReturnType())).append(' '); - sb.append(Field.getTypeName(getDeclaringClass())).append('.'); + sb.append(getReturnType().getTypeName()).append(' '); + sb.append(getDeclaringClass().getTypeName()).append('.'); sb.append(getName()); } @@ -387,16 +386,14 @@ * class name. If the method is declared to throw exceptions, the * parameter list is followed by a space, followed by the word * throws followed by a comma-separated list of the generic thrown - * exception types. If there are no type parameters, the type - * parameter list is elided. + * exception types. * *

The access modifiers are placed in canonical order as * specified by "The Java Language Specification". This is * {@code public}, {@code protected} or {@code private} first, * and then other modifiers in the following order: - * {@code abstract}, {@code static}, {@code final}, - * {@code synchronized}, {@code native}, {@code strictfp}, - * {@code default}. + * {@code abstract}, {@code default}, {@code static}, {@code final}, + * {@code synchronized}, {@code native}, {@code strictfp}. * * @return a string describing this {@code Method}, * include type parameters @@ -413,11 +410,9 @@ @Override void specificToGenericStringHeader(StringBuilder sb) { Type genRetType = getGenericReturnType(); - sb.append( ((genRetType instanceof Class)? - Field.getTypeName((Class)genRetType):genRetType.toString())) - .append(' '); + sb.append(genRetType.getTypeName()).append(' '); - sb.append(Field.getTypeName(getDeclaringClass())).append('.'); + sb.append(getDeclaringClass().getTypeName()).append('.'); sb.append(getName()); } --- old/src/share/classes/java/lang/reflect/Modifier.java 2013-04-03 00:21:07.000000000 -0700 +++ new/src/share/classes/java/lang/reflect/Modifier.java 2013-04-03 00:21:07.000000000 -0700 @@ -43,8 +43,7 @@ * @author Nakul Saraiya * @author Kenneth Russell */ -public -class Modifier { +public class Modifier { /* * Bootstrapping protocol between java.lang and java.lang.reflect @@ -393,7 +392,7 @@ * */ static final int ACCESS_MODIFIERS = - Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE; + Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE; /** * Return an {@code int} value OR-ing together the source language --- old/src/share/classes/java/lang/reflect/Parameter.java 2013-04-03 00:21:08.000000000 -0700 +++ new/src/share/classes/java/lang/reflect/Parameter.java 2013-04-03 00:21:07.000000000 -0700 @@ -110,21 +110,19 @@ public String toString() { final StringBuilder sb = new StringBuilder(); final Type type = getParameterizedType(); - final String typename = (type instanceof Class)? - Field.getTypeName((Class)type): - (type.toString()); + final String typename = type.getTypeName(); sb.append(Modifier.toString(getModifiers())); if(0 != modifiers) - sb.append(" "); + sb.append(' '); if(isVarArgs()) sb.append(typename.replaceFirst("\\[\\]$", "...")); else sb.append(typename); - sb.append(" "); + sb.append(' '); sb.append(getName()); return sb.toString(); --- old/src/share/classes/java/lang/reflect/Type.java 2013-04-03 00:21:08.000000000 -0700 +++ new/src/share/classes/java/lang/reflect/Type.java 2013-04-03 00:21:08.000000000 -0700 @@ -32,6 +32,17 @@ * * @since 1.5 */ - public interface Type { + /** + * Returns a string describing this type, including information + * about any type parameters. + * + * @implSpec The default implementation calls {@code toString}. + * + * @return a string describing this type + * @since 1.8 + */ + default String getTypeName() { + return toString(); + } } --- /dev/null 2013-04-02 12:23:00.999930644 -0700 +++ new/test/java/lang/Class/GenericStringTest.java 2013-04-03 00:21:08.000000000 -0700 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6298888 6992705 + * @summary Check Class.toGenericString() + * @author Joseph D. Darcy + */ + +import java.lang.reflect.*; +import java.lang.annotation.*; +import java.util.*; + +@ExpectedGenericString("public class GenericStringTest") +public class GenericStringTest { + public static void main(String... args){ + int failures = 0; + + failures += checkToGenericString(int.class, "int"); + + Class[] types = { + GenericStringTest.class, + AnInterface.class, + LocalMap.class, + AnEnum.class, + AnotherEnum.class, + }; + + for(Class clazz : types) { + failures += checkToGenericString(clazz, clazz.getAnnotation(ExpectedGenericString.class).value()); + } + + if (failures > 0) { + throw new RuntimeException(); + } + } + + private static int checkToGenericString(Class clazz, String expected) { + String genericString = clazz.toGenericString(); + if (!genericString.equals(expected)) { + System.err.printf("Unexpected Class.toGenericString output; expected '%s', got '%s'.%n", + expected, + genericString); + return 1; + } else + return 0; + } +} + +@Retention(RetentionPolicy.RUNTIME) +@interface ExpectedGenericString { + String value(); +} + +@ExpectedGenericString("abstract interface AnInterface") +strictfp interface AnInterface {} + +@ExpectedGenericString("abstract interface LocalMap") +interface LocalMap {} + +@ExpectedGenericString("final enum AnEnum") +enum AnEnum { + FOO; +} + +@ExpectedGenericString("enum AnotherEnum") +enum AnotherEnum { + BAR{}; +}