/* * Copyright (c) 2019, 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.reflect; import jdk.internal.access.SharedSecrets; import sun.reflect.annotation.AnnotationParser; import sun.reflect.annotation.TypeAnnotation; import sun.reflect.annotation.TypeAnnotationParser; import sun.reflect.generics.factory.CoreReflectionFactory; import sun.reflect.generics.factory.GenericsFactory; import sun.reflect.generics.repository.FieldRepository; import sun.reflect.generics.scope.ClassScope; import java.lang.annotation.Annotation; import java.util.Map; import java.util.Objects; /** * {@preview Associated with records, a preview feature of the Java language. * * This class is associated with records, a preview * feature of the Java language. Preview features * may be removed in a future release, or upgraded to permanent * features of the Java language.} * * A {@code RecordComponent} provides information about, and dynamic access to, a * component of a record class. * * @see Class#getRecordComponents() * @see java.lang.Record * @jls 8.10 Record Types * @since 14 */ @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS, essentialAPI=false) public final class RecordComponent implements AnnotatedElement { // declaring class private Class clazz; private String name; private Class type; private Method accessor; private String signature; // generic info repository; lazily initialized private transient FieldRepository genericInfo; private byte[] annotations; private byte[] typeAnnotations; @SuppressWarnings("preview") private RecordComponent root; // only the JVM can create record components private RecordComponent() {} /** * Returns the name of this record component. * * @return the name of this record component */ public String getName() { return name; } /** * Returns a {@code Class} that identifies the declared type for this * record component. * * @return a {@code Class} identifying the declared type of the component * represented by this record component */ public Class getType() { return type; } /** * Returns a {@code String} that describes the generic type signature for * this record component. * * @return a {@code String} that describes the generic type signature for * this record component * * @jvms 4.7.9.1 Signatures */ public String getGenericSignature() { return signature; } /** * Returns a {@code Type} object that represents the declared type for * this record component. * *

If the declared type of the record component is a parameterized type, * the {@code Type} object returned reflects the actual type arguments used * in the source code. * *

If the type of the underlying record component is a type variable or a * parameterized type, it is created. Otherwise, it is resolved. * * @return a {@code Type} object that represents the declared type for * this record component * @throws GenericSignatureFormatError if the generic record component * signature does not conform to the format specified in * The Java Virtual Machine Specification * @throws TypeNotPresentException if the generic type * signature of the underlying record component refers to a non-existent * type declaration * @throws MalformedParameterizedTypeException if the generic * signature of the underlying record component refers to a parameterized * type that cannot be instantiated for any reason */ public Type getGenericType() { if (getGenericSignature() != null) return getGenericInfo().getGenericType(); else return getType(); } // Accessor for generic info repository private FieldRepository getGenericInfo() { // lazily initialize repository if necessary if (genericInfo == null) { // create and cache generic info repository genericInfo = FieldRepository.make(getGenericSignature(), getFactory()); } return genericInfo; //return cached repository } // Accessor for factory private GenericsFactory getFactory() { Class c = getDeclaringRecord(); // create scope and factory return CoreReflectionFactory.make(c, ClassScope.make(c)); } /** * Returns an {@code AnnotatedType} object that represents the use of a type to specify * the declared type of this record component. * * @return an object representing the declared type of this record component */ public AnnotatedType getAnnotatedType() { return TypeAnnotationParser.buildAnnotatedType(typeAnnotations, SharedSecrets.getJavaLangAccess(). getConstantPool(getDeclaringRecord()), this, getDeclaringRecord(), getGenericType(), TypeAnnotation.TypeAnnotationTarget.FIELD); } /** * Returns a {@code Method} that represents the accessor for this record * component. * * @return a {@code Method} that represents the accessor for this record * component */ public Method getAccessor() { return accessor; } /** * {@inheritDoc} *

Note that any annotation returned by this method is a * declaration annotation. * @throws NullPointerException {@inheritDoc} */ @Override public T getAnnotation(Class annotationClass) { Objects.requireNonNull(annotationClass); return annotationClass.cast(declaredAnnotations().get(annotationClass)); } private transient volatile Map, Annotation> declaredAnnotations; private Map, Annotation> declaredAnnotations() { Map, Annotation> declAnnos; if ((declAnnos = declaredAnnotations) == null) { synchronized (this) { if ((declAnnos = declaredAnnotations) == null) { @SuppressWarnings("preview") RecordComponent root = this.root; if (root != null) { declAnnos = root.declaredAnnotations(); } else { declAnnos = AnnotationParser.parseAnnotations( annotations, SharedSecrets.getJavaLangAccess() .getConstantPool(getDeclaringRecord()), getDeclaringRecord()); } declaredAnnotations = declAnnos; } } } return declAnnos; } /** * {@inheritDoc} *

Note that any annotations returned by this method are * declaration annotations. */ @Override public Annotation[] getAnnotations() { return getDeclaredAnnotations(); } /** * {@inheritDoc} *

Note that any annotations returned by this method are * declaration annotations. */ @Override public Annotation[] getDeclaredAnnotations() { return AnnotationParser.toArray(declaredAnnotations()); } /** * Returns a string describing this record component. The format is * the record component type, followed by a space, followed by the name * of the record component. * For example: *

     *    java.lang.String name
     *    int age
     * 
* * @return a string describing this record component */ public String toString() { return (getType().getTypeName() + " " + getName()); } /** * Returns the record class which declares this record component. * * @return The record class declaring this record component. */ public Class getDeclaringRecord() { return clazz; } }