提交 27e9861d 编写于 作者: J jfranck

7154390: Add support for repeating annotations in j.l.r.AnnotatedElement

Reviewed-by: darcy
上级 ccdd069a
......@@ -48,6 +48,7 @@ import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import sun.misc.Unsafe;
import sun.reflect.ConstantPool;
import sun.reflect.Reflection;
......@@ -3044,34 +3045,62 @@ public final
* @throws NullPointerException {@inheritDoc}
* @since 1.5
*/
@SuppressWarnings("unchecked")
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
if (annotationClass == null)
throw new NullPointerException();
Objects.requireNonNull(annotationClass);
initAnnotationsIfNecessary();
return (A) annotations.get(annotationClass);
return AnnotationSupport.getOneAnnotation(annotations, annotationClass);
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.5
*/
public boolean isAnnotationPresent(
Class<? extends Annotation> annotationClass) {
if (annotationClass == null)
throw new NullPointerException();
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
Objects.requireNonNull(annotationClass);
return getAnnotation(annotationClass) != null;
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
public <A extends Annotation> A[] getAnnotations(Class<A> annotationClass) {
Objects.requireNonNull(annotationClass);
initAnnotationsIfNecessary();
return AnnotationSupport.getMultipleAnnotations(annotations, annotationClass);
}
/**
* @since 1.5
*/
public Annotation[] getAnnotations() {
initAnnotationsIfNecessary();
return AnnotationParser.toArray(annotations);
return AnnotationSupport.unpackToArray(annotations);
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) {
Objects.requireNonNull(annotationClass);
initAnnotationsIfNecessary();
return AnnotationSupport.getOneAnnotation(declaredAnnotations, annotationClass);
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
public <A extends Annotation> A[] getDeclaredAnnotations(Class<A> annotationClass) {
Objects.requireNonNull(annotationClass);
initAnnotationsIfNecessary();
return AnnotationSupport.getMultipleAnnotations(declaredAnnotations, annotationClass);
}
/**
......@@ -3079,7 +3108,17 @@ public final
*/
public Annotation[] getDeclaredAnnotations() {
initAnnotationsIfNecessary();
return AnnotationParser.toArray(declaredAnnotations);
return AnnotationSupport.unpackToArray(declaredAnnotations);
}
/** Returns one "directly" present annotation or null */
<A extends Annotation> A getDirectDeclaredAnnotation(Class<A> annotationClass) {
Objects.requireNonNull(annotationClass);
initAnnotationsIfNecessary();
@SuppressWarnings("unchecked") // TODO check safe
A ret = (A)declaredAnnotations.get(annotationClass);
return ret;
}
// Annotations cache
......
......@@ -394,6 +394,14 @@ public class Package implements java.lang.reflect.AnnotatedElement {
return getPackageInfo().isAnnotationPresent(annotationClass);
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
public <A extends Annotation> A[] getAnnotations(Class<A> annotationClass) {
return getPackageInfo().getAnnotations(annotationClass);
}
/**
* @since 1.5
*/
......@@ -401,6 +409,22 @@ public class Package implements java.lang.reflect.AnnotatedElement {
return getPackageInfo().getAnnotations();
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) {
return getPackageInfo().getDeclaredAnnotation(annotationClass);
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
public <A extends Annotation> A[] getDeclaredAnnotations(Class<A> annotationClass) {
return getPackageInfo().getDeclaredAnnotations(annotationClass);
}
/**
* @since 1.5
*/
......
......@@ -25,6 +25,7 @@
package java.lang;
import java.io.*;
import java.lang.annotation.Annotation;
import java.util.Properties;
import java.util.PropertyPermission;
import java.util.StringTokenizer;
......@@ -1195,6 +1196,9 @@ public final class System {
public AnnotationType getAnnotationType(Class<?> klass) {
return klass.getAnnotationType();
}
public <A extends Annotation> A getDirectDeclaredAnnotation(Class<?> klass, Class<A> anno) {
return klass.getDirectDeclaredAnnotation(anno);
}
public <E extends Enum<E>>
E[] getEnumConstantsShared(Class<E> klass) {
return klass.getEnumConstantsShared();
......
......@@ -26,10 +26,35 @@
package java.lang.annotation;
/**
* A meta-annotation to indicate which annotation type should be used
* as a container for repeated values of the annotation type modified
* by the {@code ContainedBy} annotation.
* The annotation type {@code java.lang.annotation.ContainedBy} is
* used to indicate that the annotation type whose declaration it
* (meta-)annotates is <em>repeatable</em>. The value of
* {@code @ContainedBy} indicates the <em>containing annotation
* type</em> for the repeatable annotation type.
*
* <p>The pair of annotation types {@code @ContainedBy} and
* {@link java.lang.annotation.ContainerFor @ContainerFor} are used to
* indicate that annotation types are repeatable. Specifically:
*
* <ul>
* <li>The annotation type {@code @ContainedBy} is used on the
* declaration of a repeatable annotation type (JLS 9.6) to indicate
* its containing annotation type.
*
* <li>The annotation type {@code @ContainerFor} is used on the
* declaration of a containing annotation type (JLS 9.6) to indicate
* the repeatable annotation type for which it serves as the
* containing annotation type.
* </ul>
*
* <p>
* An inconsistent pair of {@code @ContainedBy} and
* {@code @ContainerFor} annotations on a repeatable annotation type
* and its containing annotation type (JLS 9.6) will lead to
* compile-time errors and runtime exceptions when using reflection to
* read annotations of a repeatable type.
*
* @see java.lang.annotation.ContainerFor
* @since 1.8
* @jls 9.6 Annotation Types
* @jls 9.7 Annotations
......@@ -39,8 +64,8 @@ package java.lang.annotation;
@Target(ElementType.ANNOTATION_TYPE)
public @interface ContainedBy {
/**
* The annotation type to use to store repeated values of another
* annotation.
* Indicates the <em>containing annotation type</em> for the
* repeatable annotation type.
*/
Class<? extends Annotation> value();
}
......@@ -26,10 +26,36 @@
package java.lang.annotation;
/**
* Indicates that an annotation type is a container for repeated
* instances of annotations of the type of the value of the
* {@code ContainerFor}'s value element.
* The annotation type {@code java.lang.annotation.ContainerFor} is
* used to indicate that the annotation type whose declaration it
* (meta-)annotates is a <em>containing annotation type</em>. The
* value of {@code @ContainerFor} indicates the <em>repeatable
* annotation type</em> for the containing annotation type.
*
* <p>The pair of annotation types {@link
* java.lang.annotation.ContainedBy @ContainedBy} and
* {@code @ContainerFor} are used to indicate that annotation types
* are repeatable. Specifically:
*
* <ul>
* <li>The annotation type {@code @ContainedBy} is used on the
* declaration of a repeatable annotation type (JLS 9.6) to indicate
* its containing annotation type.
*
* <li>The annotation type {@code @ContainerFor} is used on the
* declaration of a containing annotation type (JLS 9.6) to indicate
* the repeatable annotation type for which it serves as the
* containing annotation type.
* </ul>
*
* <p>
* An inconsistent pair of {@code @ContainedBy} and
* {@code @ContainerFor} annotations on a repeatable annotation type
* and its containing annotation type (JLS 9.6) will lead to
* compile-time errors and runtime exceptions when using reflection to
* read annotations of a repeatable type.
*
* @see java.lang.annotation.ContainedBy
* @since 1.8
* @jls 9.6 Annotation Types
* @jls 9.7 Annotations
......@@ -38,9 +64,10 @@ package java.lang.annotation;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface ContainerFor {
/**
* The repeating annotation type that the annotation type
* annotated with this annotation is a container for.
* Indicates the repeatable annotation type for the containing
* annotation type.
*/
Class<? extends Annotation> value();
}
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.lang.annotation;
import java.util.Objects;
/**
* Thrown to indicate that an annotation type whose declaration is
* (meta-)annotated with a {@link ContainerFor} annotation is not, in
* fact, the <em>containing annotation type of the type named by {@link
* ContainerFor}</em>.
*
* @see java.lang.reflect.AnnotatedElement
* @since 1.8
* @jls 9.6 Annotation Types
* @jls 9.7 Annotations
*/
public class InvalidContainerAnnotationError extends AnnotationFormatError {
private static final long serialVersionUID = 5023L;
/**
* The instance of the erroneous container.
*/
private transient Annotation container;
/**
* The type of the annotation that should be contained in the
* container.
*/
private transient Class<? extends Annotation> annotationType;
/**
* Constructs a new InvalidContainerAnnotationError with the
* specified detail message.
*
* @param message the detail message.
*/
public InvalidContainerAnnotationError(String message) {
super(message);
}
/**
* Constructs a new InvalidContainerAnnotationError with the specified
* detail message and cause. Note that the detail message associated
* with {@code cause} is <i>not</i> automatically incorporated in
* this error's detail message.
*
* @param message the detail message
* @param cause the cause, may be {@code null}
*/
public InvalidContainerAnnotationError(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new InvalidContainerAnnotationError with the
* specified cause and a detail message of {@code (cause == null ?
* null : cause.toString())} (which typically contains the class
* and detail message of {@code cause}).
*
* @param cause the cause, may be {@code null}
*/
public InvalidContainerAnnotationError(Throwable cause) {
super(cause);
}
/**
* Constructs InvalidContainerAnnotationError for the specified
* container instance and contained annotation type.
*
* @param message the detail message
* @param cause the cause, may be {@code null}
* @param container the erroneous container instance, may be
* {@code null}
* @param annotationType the annotation type intended to be
* contained, may be {@code null}
*/
public InvalidContainerAnnotationError(String message,
Throwable cause,
Annotation container,
Class<? extends Annotation> annotationType) {
super(message, cause);
this.container = container;
this.annotationType = annotationType;
}
/**
* Returns the erroneous container.
*
* @return the erroneous container, may return {@code null}
*/
public Annotation getContainer() {
return container;
}
/**
* Returns the annotation type intended to be contained. Returns
* {@code null} if the annotation type intended to be contained
* could not be determined.
*
* @return the annotation type intended to be contained, or {@code
* null} if unknown
*/
public Class<? extends Annotation> getAnnotationType() {
return annotationType;
}
}
/*
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2012, 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
......@@ -184,11 +184,18 @@ public class AccessibleObject implements AnnotatedElement {
* @throws NullPointerException {@inheritDoc}
* @since 1.5
*/
public boolean isAnnotationPresent(
Class<? extends Annotation> annotationClass) {
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
public <T extends Annotation> T[] getAnnotations(Class<T> annotationClass) {
throw new AssertionError("All subclasses should override this method");
}
/**
* @since 1.5
*/
......@@ -196,6 +203,28 @@ public class AccessibleObject implements AnnotatedElement {
return getDeclaredAnnotations();
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
public <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
// Only annotations on classes are inherited, for all other
// objects getDeclaredAnnotation is the same as
// getAnnotation.
return getAnnotation(annotationClass);
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
public <T extends Annotation> T[] getDeclaredAnnotations(Class<T> annotationClass) {
// Only annotations on classes are inherited, for all other
// objects getDeclaredAnnotations is the same as
// getAnnotations.
return getAnnotations(annotationClass);
}
/**
* @since 1.5
*/
......
/*
* Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2012, 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
......@@ -45,6 +45,11 @@ import java.lang.annotation.Annotation;
* a {@link EnumConstantNotPresentException} if the enum constant in the
* annotation is no longer present in the enum type.
*
* <p>Attempting to read annotations of a repeatable annotation type T
* that are contained in an annotation whose type is not, in fact, the
* containing annotation type of T will result in an
* InvalidContainerAnnotationError.
*
* <p>Finally, Attempting to read a member whose definition has evolved
* incompatibly will result in a {@link
* java.lang.annotation.AnnotationTypeMismatchException} or an
......@@ -55,6 +60,7 @@ import java.lang.annotation.Annotation;
* @see java.lang.annotation.AnnotationFormatError
* @see java.lang.annotation.AnnotationTypeMismatchException
* @see java.lang.annotation.IncompleteAnnotationException
* @see java.lang.annotation.InvalidContainerAnnotationError
* @since 1.5
* @author Josh Bloch
*/
......@@ -86,6 +92,23 @@ public interface AnnotatedElement {
*/
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
/**
* Returns an array of all this element's annotations for the
* specified type if one or more of such annotation is present,
* else an array of length zero.
*
* The caller of this method is free to modify the returned array;
* it will have no effect on the arrays returned to other callers.
*
* @param annotationClass the Class object corresponding to the
* annotation type
* @return all this element's annotations for the specified annotation type if
* present on this element, else an array of length zero
* @throws NullPointerException if the given annotation class is null
* @since 1.8
*/
<T extends Annotation> T[] getAnnotations(Class<T> annotationClass);
/**
* Returns all annotations present on this element. (Returns an array
* of length zero if this element has no annotations.) The caller of
......@@ -97,13 +120,49 @@ public interface AnnotatedElement {
*/
Annotation[] getAnnotations();
/**
* Returns this element's annotation for the specified type if
* such an annotation is present, else null.
*
* This method ignores inherited annotations. (Returns null if no
* annotations are directly present on this element.)
*
* @param annotationClass the Class object corresponding to the
* annotation type
* @return this element's annotation for the specified annotation type if
* present on this element, else null
* @throws NullPointerException if the given annotation class is null
* @since 1.8
*/
<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass);
/**
* Returns an array of all this element's annotations for the
* specified type if one or more of such annotation is directly
* present, else an array of length zero.
*
* This method ignores inherited annotations. (Returns
* an array of length zero if no annotations are directly present
* on this element.) The caller of this method is free to modify
* the returned array; it will have no effect on the arrays
* returned to other callers.
*
* @param annotationClass the Class object corresponding to the
* annotation type
* @return all this element's annotations for the specified annotation type if
* present on this element, else an array of length zero
* @throws NullPointerException if the given annotation class is null
* @since 1.8
*/
<T extends Annotation> T[] getDeclaredAnnotations(Class<T> annotationClass);
/**
* Returns all annotations that are directly present on this
* element. Unlike the other methods in this interface, this method
* ignores inherited annotations. (Returns an array of length zero if
* no annotations are directly present on this element.) The caller of
* this method is free to modify the returned array; it will have no
* effect on the arrays returned to other callers.
* element. This method ignores inherited annotations. (Returns
* an array of length zero if no annotations are directly present
* on this element.) The caller of this method is free to modify
* the returned array; it will have no effect on the arrays
* returned to other callers.
*
* @return All annotations directly present on this element
* @since 1.5
......
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
......@@ -26,8 +26,11 @@
package java.lang.reflect;
import java.lang.annotation.*;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import sun.reflect.annotation.AnnotationParser;
import sun.reflect.annotation.AnnotationSupport;
import sun.reflect.generics.repository.ConstructorRepository;
/**
......@@ -363,19 +366,28 @@ public abstract class Executable extends AccessibleObject
* {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
if (annotationClass == null)
throw new NullPointerException();
Objects.requireNonNull(annotationClass);
return (T) declaredAnnotations().get(annotationClass);
return AnnotationSupport.getOneAnnotation(declaredAnnotations(), annotationClass);
}
/**
* {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
public <T extends Annotation> T[] getAnnotations(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
return AnnotationSupport.getMultipleAnnotations(declaredAnnotations(), annotationClass);
}
/**
* {@inheritDoc}
*/
public Annotation[] getDeclaredAnnotations() {
return AnnotationParser.toArray(declaredAnnotations());
return AnnotationSupport.unpackToArray(declaredAnnotations());
}
private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations;
......
/*
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2012, 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
......@@ -33,7 +33,9 @@ import sun.reflect.generics.factory.GenericsFactory;
import sun.reflect.generics.scope.ClassScope;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Objects;
import sun.reflect.annotation.AnnotationParser;
import sun.reflect.annotation.AnnotationSupport;
/**
......@@ -1012,19 +1014,28 @@ class Field extends AccessibleObject implements Member {
* @throws NullPointerException {@inheritDoc}
* @since 1.5
*/
@SuppressWarnings("unchecked")
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
if (annotationClass == null)
throw new NullPointerException();
Objects.requireNonNull(annotationClass);
return (T) declaredAnnotations().get(annotationClass);
return AnnotationSupport.getOneAnnotation(declaredAnnotations(), annotationClass);
}
/**
* @since 1.5
* {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
public <T extends Annotation> T[] getAnnotations(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
return AnnotationSupport.getMultipleAnnotations(declaredAnnotations(), annotationClass);
}
/**
* {@inheritDoc}
*/
public Annotation[] getDeclaredAnnotations() {
return AnnotationParser.toArray(declaredAnnotations());
return AnnotationSupport.unpackToArray(declaredAnnotations());
}
private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations;
......
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2012, 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
......@@ -25,6 +25,7 @@
package sun.misc;
import java.lang.annotation.Annotation;
import sun.reflect.ConstantPool;
import sun.reflect.annotation.AnnotationType;
import sun.nio.ch.Interruptible;
......@@ -83,4 +84,9 @@ public interface JavaLangAccess {
* Returns the ith StackTraceElement for the given throwable.
*/
StackTraceElement getStackTraceElement(Throwable t, int i);
/**
* Returns a directly present annotation.
*/
public <A extends Annotation> A getDirectDeclaredAnnotation(Class<?> klass, Class<A> anno);
}
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2012, 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
......@@ -806,4 +806,6 @@ public class AnnotationParser {
public static Annotation[] toArray(Map<Class<? extends Annotation>, Annotation> annotations) {
return annotations.values().toArray(EMPTY_ANNOTATION_ARRAY);
}
static Annotation[] getEmptyAnnotationArray() { return EMPTY_ANNOTATION_ARRAY; }
}
/*
* Copyright (c) 2012, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package sun.reflect.annotation;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import sun.reflect.Reflection;
import sun.misc.JavaLangAccess;
public final class AnnotationSupport {
private static JavaLangAccess javaLangAccess = sun.misc.SharedSecrets.getJavaLangAccess();
/**
* Finds and returns _one_ annotation of the type indicated by
* {@code annotationClass} from the {@code Map} {@code
* annotationMap}. Looks into containers of the {@code
* annotationClass} (as specified by an the {@code
* annotationClass} type being meta-annotated with an {@code
* ContainedBy} annotation).
*
* @param annotationMap the {@code Map} used to store annotations and indexed by their type
* @param annotationClass the type of annotation to search for
*
* @return in instance of {@code annotationClass} or {@code null} if none were found
*/
public static <A extends Annotation> A getOneAnnotation(final Map<Class<? extends Annotation>, Annotation> annotationMap,
final Class<A> annotationClass) {
@SuppressWarnings("unchecked")
final A candidate = (A)annotationMap.get(annotationClass);
if (candidate != null) {
return candidate;
}
final Class<? extends Annotation> containerClass = getContainer(annotationClass);
if (containerClass != null) {
return unpackOne(annotationMap.get(containerClass), annotationClass);
}
return null; // found none
}
/**
* Finds and returns all annotation of the type indicated by
* {@code annotationClass} from the {@code Map} {@code
* annotationMap}. Looks into containers of the {@code
* annotationClass} (as specified by an the {@code
* annotationClass} type being meta-annotated with an {@code
* ContainedBy} annotation).
*
* @param annotationMap the {@code Map} used to store annotations indexed by their type
* @param annotationClass the type of annotation to search for
*
* @return an array of instances of {@code annotationClass} or an empty array if none were found
*/
public static <A extends Annotation> A[] getMultipleAnnotations(final Map<Class<? extends Annotation>, Annotation> annotationMap,
final Class<A> annotationClass) {
final ArrayList<A> res = new ArrayList<A>();
@SuppressWarnings("unchecked")
final A candidate = (A)annotationMap.get(annotationClass);
if (candidate != null) {
res.add(candidate);
}
final Class<? extends Annotation> containerClass = getContainer(annotationClass);
if (containerClass != null) {
res.addAll(unpackAll(annotationMap.get(containerClass), annotationClass));
}
@SuppressWarnings("unchecked") // should be safe annotationClass is a token for A
final A[] emptyTemplateArray = (A[])Array.newInstance(annotationClass, 0);
return res.isEmpty() ? emptyTemplateArray : res.toArray(emptyTemplateArray);
}
/**
* Unpacks the {@code annotationMap} parameter into an array of
* {@code Annotation}s. This method will unpack all repeating
* annotaions containers (once). An annotation type is marked as a
* container by meta-annotating it the with the {@code
* ContainerFor} annotation.
*
* @param annotationMap the {@code Map} from where the annotations are unpacked
*
* @return an array of Annotation
*/
public static Annotation[] unpackToArray(Map<Class<? extends Annotation>, Annotation> annotationMap) {
List<Annotation> res = new ArrayList<>();
for (Map.Entry<Class<? extends Annotation>, Annotation> e : annotationMap.entrySet()) {
Class<? extends Annotation> annotationClass = e.getKey();
Annotation annotationInstance = e.getValue();
Class<? extends Annotation> containee = getContainee(e.getKey());
boolean isContainer = javaLangAccess.getDirectDeclaredAnnotation(annotationClass, ContainerFor.class) != null;
if (isContainer) {
res.addAll(unpackAll(annotationInstance, containee));
} else {
res.add(annotationInstance);
}
}
return res.isEmpty()
? AnnotationParser.getEmptyAnnotationArray()
: res.toArray(AnnotationParser.getEmptyAnnotationArray());
}
/** Helper to get the container, or null if none, of an annotation. */
private static <A extends Annotation> Class<? extends Annotation> getContainer(Class<A> annotationClass) {
ContainedBy containerAnnotation =
javaLangAccess.getDirectDeclaredAnnotation(annotationClass, ContainedBy.class);
return (containerAnnotation == null) ? null : containerAnnotation.value();
}
/** Helper to get the containee, or null if this isn't a container, of a possible container annotation. */
private static <A extends Annotation> Class<? extends Annotation> getContainee(Class<A> annotationClass) {
ContainerFor containerAnnotation =
javaLangAccess.getDirectDeclaredAnnotation(annotationClass, ContainerFor.class);
return (containerAnnotation == null) ? null : containerAnnotation.value();
}
/** Reflectively look up and get the returned array from the the
* invocation of the value() element on an instance of an
* Annotation.
*/
private static <A extends Annotation> A[] getValueArray(Annotation containerInstance) {
try {
// the spec tells us the container must have an array-valued
// value element. Get the AnnotationType, get the "value" element
// and invoke it to get the contents.
Class<?> containerClass = containerInstance.annotationType();
AnnotationType annoType = javaLangAccess.getAnnotationType(containerClass);
if (annoType == null)
throw new InvalidContainerAnnotationError(containerInstance + " is an invalid container for repeating annotations");
Method m = annoType.members().get("value");
if (m == null)
throw new InvalidContainerAnnotationError(containerInstance + " is an invalid container for repeating annotations");
m.setAccessible(true);
@SuppressWarnings("unchecked") // not provably safe, but we catch the ClassCastException
A[] a = (A[])m.invoke(containerInstance); // this will erase to (Annotation[]) but we
// do a runtime cast on the return-value
// in the methods that call this method
return a;
} catch (IllegalAccessException | // couldnt loosen security
IllegalArgumentException | // parameters doesn't match
InvocationTargetException | // the value method threw an exception
ClassCastException e) { // well, a cast failed ...
e.getCause().printStackTrace();
throw new InvalidContainerAnnotationError(containerInstance + " is an invalid container for repeating annotations",
e,
containerInstance,
null);
}
}
/* Sanity check type of and return the first annotation instance
* of type {@code annotationClass} from {@code
* containerInstance}.
*/
private static <A extends Annotation> A unpackOne(Annotation containerInstance, Class<A> annotationClass) {
if (containerInstance == null) {
return null;
}
try {
return annotationClass.cast(getValueArray(containerInstance)[0]);
} catch (ArrayIndexOutOfBoundsException | // empty array
ClassCastException | // well, a cast failed ...
NullPointerException e) { // can this NP? for good meassure
throw new InvalidContainerAnnotationError(String.format("%s is an invalid container for repeating annotations of type: %s",
containerInstance, annotationClass),
e,
containerInstance,
annotationClass);
}
}
/* Sanity check type of and return a list of all the annotation
* instances of type {@code annotationClass} from {@code
* containerInstance}.
*/
private static <A extends Annotation> List<A> unpackAll(Annotation containerInstance, Class<A> annotationClass) {
if (containerInstance == null) {
return Collections.emptyList(); // container not present
}
try {
A[] a = getValueArray(containerInstance);
ArrayList<A> l = new ArrayList<>(a.length);
for (int i = 0; i < a.length; i++)
l.add(annotationClass.cast(a[i]));
return l;
} catch (ClassCastException |
NullPointerException e) {
throw new InvalidContainerAnnotationError(String.format("%s is an invalid container for repeating annotations of type: %s",
containerInstance, annotationClass),
e,
containerInstance,
annotationClass);
}
}
}
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2012, 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
......@@ -26,6 +26,7 @@
package sun.reflect.generics.reflectiveObjects;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
......@@ -192,6 +193,25 @@ public class TypeVariableImpl<D extends GenericDeclaration>
return null;
}
public <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
return null;
}
@SuppressWarnings("unchecked")
public <T extends Annotation> T[] getAnnotations(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
// safe because annotationClass is the class for T
return (T[])Array.newInstance(annotationClass, 0);
}
@SuppressWarnings("unchecked")
public <T extends Annotation> T[] getDeclaredAnnotations(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
// safe because annotationClass is the class for T
return (T[])Array.newInstance(annotationClass, 0);
}
public Annotation[] getAnnotations() {
// Since zero-length, don't need defensive clone
return EMPTY_ANNOTATION_ARRAY;
......
/*
* Copyright (c) 2012, 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 7154390
* @summary Unit test for repeated annotation reflection
*
* @compile RepeatedUnitTest.java subpackage/package-info.java subpackage/Container.java subpackage/Containee.java subpackage/NonRepeated.java subpackage/InheritedContainee.java subpackage/InheritedContainer.java subpackage/InheritedNonRepeated.java
* @run main RepeatedUnitTest
*/
import subpackage.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
public class RepeatedUnitTest {
public static void main(String[] args) throws Exception {
// PACKAGE ANNOTATIONS
Class c = Class.forName("subpackage.NonRepeated"); // force package "subpackage" load
Package p = Package.getPackage("subpackage");
packageNonRepeated(p);
packageRepeated(p);
packageContainer(p);
// INHERITED/NON-INHERITED ON CLASS
inheritedMe1();
inheritedMe2();
inheritedMe3();
inheritedMe4();
// CONSTRUCTOR
checkMultiplier(Me1.class.getConstructor(new Class[0]), 10);
// FIELD
checkMultiplier(Me1.class.getField("foo"), 1);
// METHOD
checkMultiplier(Me1.class.getDeclaredMethod("mee", null), 100);
// INNER CLASS
checkMultiplier(Me1.MiniMee.class, 1000);
// ENUM ELEMENT
checkMultiplier(Me1.E.class.getField("EE"), 10000);
// ENUM
checkMultiplier(Me1.E.class, 100000);
}
static void packageNonRepeated(AnnotatedElement e) {
NonRepeated nr = e.getAnnotation(NonRepeated.class);
check(nr.value() == 10);
check(1 == countAnnotation(e, NonRepeated.class));
nr = e.getAnnotations(NonRepeated.class)[0];
check(nr.value() == 10);
check(1 == containsAnnotationOfType(e.getAnnotations(), NonRepeated.class));
}
static void packageRepeated(AnnotatedElement e) {
Containee c = e.getAnnotation(Containee.class);
check(c.value() == 1);
check(2 == countAnnotation(e, Containee.class));
c = e.getAnnotations(Containee.class)[0];
check(c.value() == 1);
c = e.getAnnotations(Containee.class)[1];
check(c.value() == 2);
check(2 == containsAnnotationOfType(e.getAnnotations(), Containee.class));
}
static void packageContainer(AnnotatedElement e) {
Container cr = e.getAnnotation(Container.class);
check(null != cr);
check(1 == containsAnnotationOfType(e.getAnnotations(Container.class), Container.class));
check(1 == countAnnotation(e, Container.class));
}
static void inheritedMe1() {
AnnotatedElement e = Me1.class;
check(null == e.getAnnotation(NonRepeated.class));
check(e.getAnnotation(InheritedNonRepeated.class).value() == 20);
check(0 == countAnnotation(e, Containee.class));
check(4 == countAnnotation(e, InheritedContainee.class));
check(0 == countAnnotation(e, Container.class));
check(1 == countAnnotation(e, InheritedContainer.class));
}
static void inheritedMe2() {
AnnotatedElement e = Me2.class;
check(e.getAnnotation(NonRepeated.class).value() == 100);
check(e.getAnnotation(InheritedNonRepeated.class).value() == 200);
check(4 == countAnnotation(e, Containee.class));
check(4 == countAnnotation(e, InheritedContainee.class));
check(1 == countAnnotation(e, Container.class));
check(1 == countAnnotation(e, InheritedContainer.class));
check(1 == countAnnotation(e, NonRepeated.class));
check(1 == countAnnotation(e, InheritedNonRepeated.class));
check(e.getAnnotations(Containee.class)[2].value() == 300);
check(e.getAnnotations(InheritedContainee.class)[2].value() == 300);
check(e.getAnnotations(InheritedNonRepeated.class)[0].value() == 200);
check(e.getAnnotations(NonRepeated.class)[0].value() == 100);
}
static void inheritedMe3() {
AnnotatedElement e = Me3.class;
check(null == e.getAnnotation(NonRepeated.class));
check(0 == countAnnotation(e, Containee.class));
check(4 == countAnnotation(e, InheritedContainee.class));
check(0 == countAnnotation(e, Container.class));
check(1 == countAnnotation(e, InheritedContainer.class));
check(e.getAnnotations(InheritedContainee.class)[2].value() == 350);
check(e.getAnnotations(InheritedNonRepeated.class)[0].value() == 15);
}
static void inheritedMe4() {
AnnotatedElement e = Me4.class;
check(e.getAnnotation(NonRepeated.class).value() == 1000);
check(e.getAnnotation(InheritedNonRepeated.class).value() == 2000);
check(4 == countAnnotation(e, Containee.class));
check(4 == countAnnotation(e, InheritedContainee.class));
check(1 == countAnnotation(e, Container.class));
check(1 == countAnnotation(e, InheritedContainer.class));
check(1 == countAnnotation(e, NonRepeated.class));
check(1 == countAnnotation(e, InheritedNonRepeated.class));
check(e.getAnnotations(Containee.class)[2].value() == 3000);
check(e.getAnnotations(InheritedContainee.class)[2].value() == 3000);
check(e.getAnnotations(InheritedNonRepeated.class)[0].value() == 2000);
check(e.getAnnotations(NonRepeated.class)[0].value() == 1000);
}
static void checkMultiplier(AnnotatedElement e, int m) {
check(e.getAnnotation(NonRepeated.class).value() == 5 * m);
check(4 == countAnnotation(e, Containee.class));
check(1 == countAnnotation(e, Container.class));
check(1 == countAnnotation(e, NonRepeated.class));
check(e.getAnnotations(Containee.class)[2].value() == 3 * m);
check(e.getAnnotations(NonRepeated.class)[0].value() == 5 * m);
}
static void check(Boolean b) {
if (!b) throw new RuntimeException();
}
static int countAnnotation(AnnotatedElement e, Class<? extends Annotation> c) {
return containsAnnotationOfType(e.getAnnotations(c), c);
}
static <A extends Annotation> int containsAnnotationOfType(A[] l, Class<? extends Annotation> a) {
int count = 0;
for (Annotation an : l) {
if (an.annotationType().equals(a))
count++;
}
return count;
}
}
@NonRepeated @InheritedNonRepeated
@InheritedContainee(1) @InheritedContainee(2) @InheritedContainee(3) @InheritedContainee(4)
@Containee(1) @Containee(2) @Containee(3) @Containee(4)
class Grandma {}
class Mother extends Grandma {}
@NonRepeated(5) @InheritedNonRepeated(15)
@InheritedContainee(150) @InheritedContainee(250) @InheritedContainee(350) @InheritedContainee(450)
@Containee(150) @Containee(250) @Containee(350) @Containee(450)
class Father extends Grandma {}
class Me1 extends Mother {
@NonRepeated(5)
@Containee(1) @Containee(2) @Containee(3) @Containee(4)
public String foo = "";
@NonRepeated(50)
@Containee(10) @Containee(20) @Containee(30) @Containee(40)
public Me1() {
}
@NonRepeated(500)
@Containee(100) @Containee(200) @Containee(300) @Containee(400)
public void mee() {
}
@NonRepeated(5000)
@Containee(1000) @Containee(2000) @Containee(3000) @Containee(4000)
public class MiniMee {}
@NonRepeated(500000)
@Containee(100000) @Containee(200000) @Containee(300000) @Containee(400000)
public enum E {
@NonRepeated(50000)
@Containee(10000) @Containee(20000) @Containee(30000) @Containee(40000)
EE(),
}
}
@NonRepeated(100) @InheritedNonRepeated(200)
@InheritedContainee(100) @InheritedContainee(200) @InheritedContainee(300) @InheritedContainee(400)
@Containee(100) @Containee(200) @Containee(300) @Containee(400)
class Me2 extends Mother {}
class Me3 extends Father {}
@NonRepeated(1000) @InheritedNonRepeated(2000)
@InheritedContainee(1000) @InheritedContainee(2000) @InheritedContainee(3000) @InheritedContainee(4000)
@Containee(1000) @Containee(2000) @Containee(3000) @Containee(4000)
class Me4 extends Father {}
/*
* Copyright (c) 2012, 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.
*/
package subpackage;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@ContainedBy(Container.class)
public @interface Containee {
int value();
}
/*
* Copyright (c) 2012, 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.
*/
package subpackage;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@ContainerFor(Containee.class)
public @interface Container {
Containee[] value();
}
/*
* Copyright (c) 2012, 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.
*/
package subpackage;
import java.lang.annotation.*;
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@ContainedBy(InheritedContainer.class)
public @interface InheritedContainee {
int value();
}
/*
* Copyright (c) 2012, 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.
*/
package subpackage;
import java.lang.annotation.*;
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@ContainerFor(InheritedContainee.class)
public @interface InheritedContainer {
InheritedContainee[] value();
}
/*
* Copyright (c) 2012, 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.
*/
package subpackage;
import java.lang.annotation.*;
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritedNonRepeated {
int value() default 20;
}
/*
* Copyright (c) 2012, 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.
*/
package subpackage;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
public @interface NonRepeated {
int value() default 10;
}
/*
* Copyright (c) 2012, 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.
*/
@NonRepeated @Containee(1) @Containee(2)
package subpackage;
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册