JDK-6519115 : MirroredTypeException thrown but should be MirroredTypesException
  • Type: Bug
  • Component: core-libs
  • Sub-Component: javax.lang.model
  • Affected Version: 6,6u10,6u29,7
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic,linux,windows_7
  • CPU: generic,x86
  • Submitted: 2007-01-30
  • Updated: 2017-05-16
  • Resolved: 2011-03-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b100Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0"
Java(TM) SE Runtime Environment (build 1.6.0-b105)
Java HotSpot(TM) Client VM (build 1.6.0-b105, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Linux nacar 2.6.18-suspend2-r1 #3 PREEMPT Tue Dec 26 21:51:39 COT 2006 i686 Intel(R) Pentium(R) M processor 2.00GHz GenuineIntel GNU/Linux

A DESCRIPTION OF THE PROBLEM :
Having an annotation that contain an element whose value is of type Class[], attempting to read this value results in a MirroredTypeException and not a MirroredTypesException as specified ( http://java.sun.com/javase/6/docs/api/javax/lang/model/element/Element.html#getAnnotation(java.lang.Class) )

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create file AnnotationBug.java
---
import java.util.Set;
import javax.annotation.processing.*;
import javax.lang.model.element.*;

@interface A {
    Class[] value() default {String.class, Number.class};
}

@SupportedAnnotationTypes({"A"})
@A
public class AnnotationBug extends AbstractProcessor {
	public boolean process(Set<? extends TypeElement> annotations,
	                       RoundEnvironment roundEnv) {
		if (!roundEnv.processingOver()) {
			Set<? extends Element> elements = roundEnv.getRootElements();
			for (Element e : elements) {
				A a;
				if ((a= e.getAnnotation(A.class)) != null) {
					System.err.println(a.value());
				}
			}
		}
		return true;
	}
}
---

2. Run:
---
javac -processor AnnotationBug AnnotationBug.java
---
...
An annotation processor threw an uncaught exception.
Consult the following stack trace for details.
javax.lang.model.type.MirroredTypeException: Attempt to access Class object for TypeMirror java.lang.String
...
---
3. :(

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
---
...
An annotation processor threw an uncaught exception.
Consult the following stack trace for details.
javax.lang.model.type.MirroredTypesException:...
...
---
:)
ACTUAL -
---
...
An annotation processor threw an uncaught exception.
Consult the following stack trace for details.
javax.lang.model.type.MirroredTypeException: Attempt to access Class object for TypeMirror java.lang.String
...
---
:(

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.Set;
import javax.annotation.processing.*;
import javax.lang.model.element.*;

@interface A {
    Class[] value() default {String.class, Number.class};
}

@SupportedAnnotationTypes({"A"})
@A
public class AnnotationBug extends AbstractProcessor {
	public boolean process(Set<? extends TypeElement> annotations,
	                       RoundEnvironment roundEnv) {
		if (!roundEnv.processingOver()) {
			Set<? extends Element> elements = roundEnv.getRootElements();
			for (Element e : elements) {
				A a;
				if ((a= e.getAnnotation(A.class)) != null) {
					System.err.println(a.value());
				}
			}
		}
		return true;
	}
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Do not use Class[] annotation members ever ever.
:(

Comments
PUBLIC COMMENTS See http://hg.openjdk.java.net/jdk7/tl/langtools/rev/852d8bb356bc
04-06-2010

SUGGESTED FIX # HG changeset patch # User darcy # Date 1275620172 25200 # Node ID 852d8bb356bcc874df99d66192b5d2ba9c7a0f4a # Parent 559c9a37d9f6d50de644e8e53dc2788d5022c07a 6519115: MirroredTypeException thrown but should be MirroredTypesException Reviewed-by: jjg --- a/src/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java Thu Jun 03 17:14:20 2010 -0700 +++ b/src/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java Thu Jun 03 19:56:12 2010 -0700 @@ -181,16 +181,16 @@ public class AnnotationProxyMaker { } public void visitArray(Attribute.Array a) { - Name elemName = ((ArrayType) a.type).elemtype.tsym.name; - - if (elemName == elemName.table.names.java_lang_Class) { // Class[] + Name elemName = ((ArrayType) a.type).elemtype.tsym.getQualifiedName(); + + if (elemName.equals(elemName.table.names.java_lang_Class)) { // Class[] // Construct a proxy for a MirroredTypesException - List<TypeMirror> elems = List.nil(); + ListBuffer<TypeMirror> elems = new ListBuffer<TypeMirror>(); for (Attribute value : a.values) { Type elem = ((Attribute.Class) value).type; - elems.add(elem); - } - value = new MirroredTypesExceptionProxy(elems); + elems.append(elem); + } + value = new MirroredTypesExceptionProxy(elems.toList()); } else { int len = a.values.length; --- a/src/share/classes/javax/lang/model/type/MirroredTypeException.java Thu Jun 03 17:14:20 2010 -0700 +++ b/src/share/classes/javax/lang/model/type/MirroredTypeException.java Thu Jun 03 19:56:12 2010 -0700 @@ -42,7 +42,7 @@ import javax.lang.model.element.Element; * @see Element#getAnnotation(Class) * @since 1.6 */ -public class MirroredTypeException extends RuntimeException { +public class MirroredTypeException extends MirroredTypesException { private static final long serialVersionUID = 269; @@ -54,7 +54,7 @@ public class MirroredTypeException exten * @param type the type being accessed */ public MirroredTypeException(TypeMirror type) { - super("Attempt to access Class object for TypeMirror " + type.toString()); + super("Attempt to access Class object for TypeMirror " + type.toString(), type); this.type = type; } @@ -76,5 +76,6 @@ public class MirroredTypeException exten throws IOException, ClassNotFoundException { s.defaultReadObject(); type = null; + types = null; } } --- a/src/share/classes/javax/lang/model/type/MirroredTypesException.java Thu Jun 03 17:14:20 2010 -0700 +++ b/src/share/classes/javax/lang/model/type/MirroredTypesException.java Thu Jun 03 19:56:12 2010 -0700 @@ -49,7 +49,17 @@ public class MirroredTypesException exte private static final long serialVersionUID = 269; - private transient List<? extends TypeMirror> types; // cannot be serialized + transient List<? extends TypeMirror> types; // cannot be serialized + + /* + * Trusted constructor to be called by MirroredTypeException. + */ + MirroredTypesException(String message, TypeMirror type) { + super(message); + List<TypeMirror> tmp = (new ArrayList<TypeMirror>()); + tmp.add(type); + types = Collections.unmodifiableList(tmp); + } /** * Constructs a new MirroredTypesException for the specified types. --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/processing/model/type/MirroredTypeEx/Plurality.java Thu Jun 03 19:56:12 2010 -0700 @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2010, 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 6519115 + * @summary Verify MirroredTypeException vs MirroredTypesException is thrown + * @compile Plurality.java + * @compile -processor Plurality -proc:only Plurality.java + * @author Joseph D. Darcy + */ +import java.lang.annotation.*; +import java.math.BigDecimal; +import java.util.*; +import javax.annotation.processing.*; +import javax.lang.model.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.lang.model.util.*; + +@SupportedAnnotationTypes("*") +@P0 +@P1 +@P2 +@S1 +public class Plurality extends AbstractProcessor { + private boolean executed = false; + + Elements elements; + Types types; + + @Override + public void init(ProcessingEnvironment penv) { + super.init(penv); + elements = penv.getElementUtils(); + types = penv.getTypeUtils(); + } + + + public boolean process(Set<? extends TypeElement> annotations, + RoundEnvironment roundEnv) { + if (!roundEnv.processingOver()) { + executed = true; + // Processing just this type + Element e = elements.getTypeElement("Plurality"); + Class[] classes = null; + + P0 p0 = e.getAnnotation(P0.class); + try { + classes = p0.value(); + } catch (MirroredTypesException mtse) { + if (mtse instanceof MirroredTypeException) { + throw new RuntimeException("Wrong exception type!"); + } + + List<? extends TypeMirror> types = mtse.getTypeMirrors(); + if (types.size() != 0) + throw new RuntimeException("List size != 0: " + + types); + } + + P1 p1 = e.getAnnotation(P1.class); + try { + classes = p1.value(); + } catch (MirroredTypesException mtse) { + if (mtse instanceof MirroredTypeException) { + throw new RuntimeException("Wrong exception type!"); + } + + List<? extends TypeMirror> types = mtse.getTypeMirrors(); + if (types.size() != 1) + throw new RuntimeException("List size != 1: " + + types); + checkTypeListMatchesClasses(types, + this.getClass().getAnnotation(P1.class).value()); + } + + + P2 p2 = e.getAnnotation(P2.class); + try { + classes = p2.value(); + } catch(MirroredTypesException mtse) { + if (mtse instanceof MirroredTypeException) { + throw new RuntimeException("Wrong exception type!"); + } + + List<? extends TypeMirror> types = mtse.getTypeMirrors(); + if (types.size() != 2) + throw new RuntimeException("List size != 2: " + + types); + checkTypeListMatchesClasses(types, + this.getClass().getAnnotation(P2.class).value()); + } + + Class<?> clazz = null; + S1 s1 = e.getAnnotation(S1.class); + try { + clazz = s1.value(); + } catch(MirroredTypesException mtse) { + List<? extends TypeMirror> types = mtse.getTypeMirrors(); + if (types.size() != 1) + throw new RuntimeException("List size != 1: " + + types); + Class<?>[] clazzes = new Class<?>[1]; + clazzes[0] = this.getClass().getAnnotation(S1.class).value(); + checkTypeListMatchesClasses(types, + clazzes); + } + + try { + clazz = s1.value(); + } catch(MirroredTypeException mte) { + TypeMirror type = mte.getTypeMirror(); + if (type == null) { + throw new RuntimeException("Expected null"); + } + List<TypeMirror> types = new ArrayList<>(); + types.add(type); + Class<?>[] clazzes = new Class<?>[1]; + clazzes[0] = this.getClass().getAnnotation(S1.class).value(); + checkTypeListMatchesClasses(types, clazzes); + } + } else { + if (!executed) { + throw new RuntimeException("Didn't seem to do anything!"); + } + } + return true; + } + + private static void checkTypeListMatchesClasses(List<? extends TypeMirror> types, + Class<?>[] classes) { + if (types.size() != classes.length) + throw new RuntimeException("Size mismatch:\n\t" + types + + "\n\t" + Arrays.toString(classes)); + int i = -1; + for(Class<?> clazz : classes) { + i++; + String canonicalName = clazz.getCanonicalName(); + String toStringName = types.get(i).toString(); + if (!canonicalName.equals(toStringName)) + throw new RuntimeException("Mismatched names: " + + canonicalName + "\t" + + toStringName); + } + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } +} + +@Retention(RetentionPolicy.RUNTIME) +@interface P0 { + Class[] value() default {}; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface P1 { + Class[] value() default {Integer.class}; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface P2 { + Class[] value() default {String.class, Number.class}; +} + +@Retention(RetentionPolicy.RUNTIME) +@interface S1 { + Class value() default BigDecimal.class; +}
04-06-2010

WORK AROUND You can process Class[]-valued annotation members. But currently you must use Element.getAnnotationMirrors; Element.getAnnotation will be useless for this member.
15-01-2009

EVALUATION Will investigate.
30-01-2007