1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.reflect;
  27 
  28 import jdk.internal.access.SharedSecrets;
  29 import sun.reflect.annotation.AnnotationParser;
  30 import sun.reflect.annotation.TypeAnnotation;
  31 import sun.reflect.annotation.TypeAnnotationParser;
  32 import sun.reflect.generics.factory.CoreReflectionFactory;
  33 import sun.reflect.generics.factory.GenericsFactory;
  34 import sun.reflect.generics.repository.FieldRepository;
  35 import sun.reflect.generics.scope.ClassScope;
  36 import java.lang.annotation.Annotation;
  37 import java.util.Map;
  38 import java.util.Objects;
  39 
  40 /**
  41  * {@preview Associated with records, a preview feature of the Java language.
  42  *
  43  *           This class is associated with <i>records</i>, a preview
  44  *           feature of the Java language. Preview features
  45  *           may be removed in a future release, or upgraded to permanent
  46  *           features of the Java language.}
  47  *
  48  * A {@code RecordComponent} provides information about, and dynamic access to, a
  49  * component of a record class.
  50  *
  51  * @see Class#getRecordComponents()
  52  * @see java.lang.Record
  53  * @jls 8.10 Record Types
  54  * @since 14
  55  */
  56 @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
  57                              essentialAPI=false)
  58 public final class RecordComponent implements AnnotatedElement {
  59     // declaring class
  60     private Class<?> clazz;
  61     private String name;
  62     private Class<?> type;
  63     private Method accessor;
  64     private String signature;
  65     // generic info repository; lazily initialized
  66     private transient FieldRepository genericInfo;
  67     private byte[] annotations;
  68     private byte[] typeAnnotations;
  69     @SuppressWarnings("preview")
  70     private RecordComponent root;
  71 
  72     // only the JVM can create record components
  73     private RecordComponent() {}
  74 
  75     /**
  76      * Returns the name of this record component.
  77      *
  78      * @return the name of this record component
  79      */
  80     public String getName() {
  81         return name;
  82     }
  83 
  84     /**
  85      * Returns a {@code Class} that identifies the declared type for this
  86      * record component.
  87      *
  88      * @return a {@code Class} identifying the declared type of the component
  89      * represented by this record component
  90      */
  91     public Class<?> getType() {
  92         return type;
  93     }
  94 
  95     /**
  96      * Returns a {@code String} that describes the  generic type signature for
  97      * this record component.
  98      *
  99      * @return a {@code String} that describes the generic type signature for
 100      * this record component
 101      *
 102      * @jvms 4.7.9.1 Signatures
 103      */
 104     public String getGenericSignature() {
 105         return signature;
 106     }
 107 
 108     /**
 109      * Returns a {@code Type} object that represents the declared type for
 110      * this record component.
 111      *
 112      * <p>If the declared type of the record component is a parameterized type,
 113      * the {@code Type} object returned reflects the actual type arguments used
 114      * in the source code.
 115      *
 116      * <p>If the type of the underlying record component is a type variable or a
 117      * parameterized type, it is created. Otherwise, it is resolved.
 118      *
 119      * @return a {@code Type} object that represents the declared type for
 120      *         this record component
 121      * @throws GenericSignatureFormatError if the generic record component
 122      *         signature does not conform to the format specified in
 123      *         <cite>The Java&trade; Virtual Machine Specification</cite>
 124      * @throws TypeNotPresentException if the generic type
 125      *         signature of the underlying record component refers to a non-existent
 126      *         type declaration
 127      * @throws MalformedParameterizedTypeException if the generic
 128      *         signature of the underlying record component refers to a parameterized
 129      *         type that cannot be instantiated for any reason
 130      */
 131     public Type getGenericType() {
 132         if (getGenericSignature() != null)
 133             return getGenericInfo().getGenericType();
 134         else
 135             return getType();
 136     }
 137 
 138     // Accessor for generic info repository
 139     private FieldRepository getGenericInfo() {
 140         // lazily initialize repository if necessary
 141         if (genericInfo == null) {
 142             // create and cache generic info repository
 143             genericInfo = FieldRepository.make(getGenericSignature(), getFactory());
 144         }
 145         return genericInfo; //return cached repository
 146     }
 147 
 148     // Accessor for factory
 149     private GenericsFactory getFactory() {
 150         Class<?> c = getDeclaringRecord();
 151         // create scope and factory
 152         return CoreReflectionFactory.make(c, ClassScope.make(c));
 153     }
 154 
 155     /**
 156      * Returns an {@code AnnotatedType} object that represents the use of a type to specify
 157      * the declared type of this record component.
 158      *
 159      * @return an object representing the declared type of this record component
 160      */
 161     public AnnotatedType getAnnotatedType() {
 162         return TypeAnnotationParser.buildAnnotatedType(typeAnnotations,
 163                 SharedSecrets.getJavaLangAccess().
 164                         getConstantPool(getDeclaringRecord()),
 165                 this,
 166                 getDeclaringRecord(),
 167                 getGenericType(),
 168                 TypeAnnotation.TypeAnnotationTarget.FIELD);
 169     }
 170 
 171     /**
 172      * Returns a {@code Method} that represents the accessor for this record
 173      * component.
 174      *
 175      * @return a {@code Method} that represents the accessor for this record
 176      * component
 177      */
 178     public Method getAccessor() {
 179         return accessor;
 180     }
 181 
 182     /**
 183      * {@inheritDoc}
 184      * <p>Note that any annotation returned by this method is a
 185      * declaration annotation.
 186      * @throws NullPointerException {@inheritDoc}
 187      */
 188     @Override
 189     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
 190         Objects.requireNonNull(annotationClass);
 191         return annotationClass.cast(declaredAnnotations().get(annotationClass));
 192     }
 193 
 194     private transient volatile Map<Class<? extends Annotation>, Annotation> declaredAnnotations;
 195 
 196     private Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {
 197         Map<Class<? extends Annotation>, Annotation> declAnnos;
 198         if ((declAnnos = declaredAnnotations) == null) {
 199             synchronized (this) {
 200                 if ((declAnnos = declaredAnnotations) == null) {
 201                     @SuppressWarnings("preview")
 202                     RecordComponent root = this.root;
 203                     if (root != null) {
 204                         declAnnos = root.declaredAnnotations();
 205                     } else {
 206                         declAnnos = AnnotationParser.parseAnnotations(
 207                                 annotations,
 208                                 SharedSecrets.getJavaLangAccess()
 209                                         .getConstantPool(getDeclaringRecord()),
 210                                 getDeclaringRecord());
 211                     }
 212                     declaredAnnotations = declAnnos;
 213                 }
 214             }
 215         }
 216         return declAnnos;
 217     }
 218 
 219     /**
 220      * {@inheritDoc}
 221      * <p>Note that any annotations returned by this method are
 222      * declaration annotations.
 223      */
 224     @Override
 225     public Annotation[] getAnnotations() {
 226         return getDeclaredAnnotations();
 227     }
 228 
 229     /**
 230      * {@inheritDoc}
 231      * <p>Note that any annotations returned by this method are
 232      * declaration annotations.
 233      */
 234     @Override
 235     public Annotation[] getDeclaredAnnotations() { return AnnotationParser.toArray(declaredAnnotations()); }
 236 
 237     /**
 238      * Returns a string describing this record component. The format is
 239      * the record component type, followed by a space, followed by the name
 240      * of the record component.
 241      * For example:
 242      * <pre>
 243      *    java.lang.String name
 244      *    int age
 245      * </pre>
 246      *
 247      * @return a string describing this record component
 248      */
 249     public String toString() {
 250         return (getType().getTypeName() + " " + getName());
 251     }
 252 
 253     /**
 254      * Returns the record class which declares this record component.
 255      *
 256      * @return The record class declaring this record component.
 257      */
 258     public Class<?> getDeclaringRecord() {
 259         return clazz;
 260     }
 261 }