69 70 /** Helper to get the container, or null if none, of an annotation. */ 71 private static <A extends Annotation> Class<? extends Annotation> getContainer(Class<A> annotationClass) { 72 Repeatable containingAnnotation = annotationClass.getDeclaredAnnotation(Repeatable.class); 73 return (containingAnnotation == null) ? null : containingAnnotation.value(); 74 } 75 76 /** Reflectively look up and get the returned array from the the 77 * invocation of the value() element on an instance of an 78 * Annotation. 79 */ 80 private static <A extends Annotation> A[] getValueArray(Annotation containerInstance) { 81 try { 82 // the spec tells us the container must have an array-valued 83 // value element. Get the AnnotationType, get the "value" element 84 // and invoke it to get the contents. 85 86 Class<? extends Annotation> containerClass = containerInstance.annotationType(); 87 AnnotationType annoType = AnnotationType.getInstance(containerClass); 88 if (annoType == null) 89 throw new InvalidContainerAnnotationError(containerInstance + " is an invalid container for repeating annotations"); 90 91 Method m = annoType.members().get("value"); 92 if (m == null) 93 throw new InvalidContainerAnnotationError(containerInstance + 94 " is an invalid container for repeating annotations"); 95 m.setAccessible(true); 96 97 @SuppressWarnings("unchecked") // not provably safe, but we catch the ClassCastException 98 A[] a = (A[])m.invoke(containerInstance); // this will erase to (Annotation[]) but we 99 // do a runtime cast on the return-value 100 // in the methods that call this method 101 return a; 102 } catch (IllegalAccessException | // couldnt loosen security 103 IllegalArgumentException | // parameters doesn't match 104 InvocationTargetException | // the value method threw an exception 105 ClassCastException e) { // well, a cast failed ... 106 throw new InvalidContainerAnnotationError( 107 containerInstance + " is an invalid container for repeating annotations", 108 e, 109 containerInstance, 110 null); 111 } 112 } 113 114 /* Sanity check type of and return a list of all the annotation 115 * instances of type {@code annotationClass} from {@code 116 * containerInstance}. 117 */ 118 private static <A extends Annotation> List<A> unpackAll(Annotation containerInstance, 119 Class<A> annotationClass) { 120 if (containerInstance == null) { 121 return Collections.emptyList(); // container not present 122 } 123 124 try { 125 A[] a = getValueArray(containerInstance); 126 List<A> l = new ArrayList<>(a.length); 127 for (int i = 0; i < a.length; i++) 128 l.add(annotationClass.cast(a[i])); 129 return l; 130 } catch (ClassCastException | 131 NullPointerException e) { 132 throw new InvalidContainerAnnotationError( 133 String.format("%s is an invalid container for repeating annotations of type: %s", 134 containerInstance, annotationClass), 135 e, 136 containerInstance, 137 annotationClass); 138 } 139 } 140 } | 69 70 /** Helper to get the container, or null if none, of an annotation. */ 71 private static <A extends Annotation> Class<? extends Annotation> getContainer(Class<A> annotationClass) { 72 Repeatable containingAnnotation = annotationClass.getDeclaredAnnotation(Repeatable.class); 73 return (containingAnnotation == null) ? null : containingAnnotation.value(); 74 } 75 76 /** Reflectively look up and get the returned array from the the 77 * invocation of the value() element on an instance of an 78 * Annotation. 79 */ 80 private static <A extends Annotation> A[] getValueArray(Annotation containerInstance) { 81 try { 82 // the spec tells us the container must have an array-valued 83 // value element. Get the AnnotationType, get the "value" element 84 // and invoke it to get the contents. 85 86 Class<? extends Annotation> containerClass = containerInstance.annotationType(); 87 AnnotationType annoType = AnnotationType.getInstance(containerClass); 88 if (annoType == null) 89 throw new AnnotationFormatError(containerInstance + " is an invalid container for repeating annotations"); 90 91 Method m = annoType.members().get("value"); 92 if (m == null) 93 throw new AnnotationFormatError(containerInstance + 94 " is an invalid container for repeating annotations"); 95 m.setAccessible(true); 96 97 @SuppressWarnings("unchecked") // not provably safe, but we catch the ClassCastException 98 A[] a = (A[])m.invoke(containerInstance); // this will erase to (Annotation[]) but we 99 // do a runtime cast on the return-value 100 // in the methods that call this method 101 return a; 102 } catch (IllegalAccessException | // couldnt loosen security 103 IllegalArgumentException | // parameters doesn't match 104 InvocationTargetException | // the value method threw an exception 105 ClassCastException e) { // well, a cast failed ... 106 throw new AnnotationFormatError( 107 containerInstance + " is an invalid container for repeating annotations", 108 e); 109 } 110 } 111 112 /* Sanity check type of and return a list of all the annotation 113 * instances of type {@code annotationClass} from {@code 114 * containerInstance}. 115 */ 116 private static <A extends Annotation> List<A> unpackAll(Annotation containerInstance, 117 Class<A> annotationClass) { 118 if (containerInstance == null) { 119 return Collections.emptyList(); // container not present 120 } 121 122 try { 123 A[] a = getValueArray(containerInstance); 124 List<A> l = new ArrayList<>(a.length); 125 for (int i = 0; i < a.length; i++) 126 l.add(annotationClass.cast(a[i])); 127 return l; 128 } catch (ClassCastException | 129 NullPointerException e) { 130 throw new AnnotationFormatError( 131 String.format("%s is an invalid container for repeating annotations of type: %s", 132 containerInstance, annotationClass), 133 e); 134 } 135 } 136 } |