All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.lukehutch.fastclasspathscanner.json.ClassFields Maven / Gradle / Ivy

Go to download

Uber-fast, ultra-lightweight Java classpath scanner. Scans the classpath by parsing the classfile binary format directly rather than by using reflection. See https://github.com/lukehutch/fast-classpath-scanner

There is a newer version: 4.0.0-beta-7
Show newest version
/*
 * This file is part of FastClasspathScanner.
 *
 * Author: Luke Hutchison
 *
 * Hosted at: https://github.com/lukehutch/fast-classpath-scanner
 *
 * --
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2018 Luke Hutchison
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without
 * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
 * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
 * OR OTHER DEALINGS IN THE SOFTWARE.
 */
package io.github.lukehutch.fastclasspathscanner.json;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * The list of fields that can be (de)serialized (non-final, non-transient, non-synthetic, accessible), and their
 * corresponding resolved (concrete) types.
 */
class ClassFields {
    /**
     * The list of fields that can be (de)serialized (non-final, non-transient, non-synthetic, accessible), and
     * their corresponding resolved (concrete) types.
     * 
     * 

* For arrays, the {@link Type} will be a {@code Class} reference where {@link Class#isArray()} is true, and * {@link Class#getComponentType()} is the element type (the element type will itself be an array-typed * {@code Class} reference for multi-dimensional arrays). * *

* For generics, the {@link Type} will be an implementation of {@link ParameterizedType}. */ final List fieldOrder = new ArrayList<>(); /** Map from field name to field and resolved type. */ final Map fieldNameToFieldTypeInfo = new HashMap<>(); /** If non-null, this is the field that has an {@link Id} annotation. */ Field idField; public ClassFields(final Class cls, final boolean resolveTypes, final boolean onlySerializePublicFields, final ClassFieldCache classFieldCache) { // Find declared accessible fields in all superclasses, and resolve generic types final Set visibleFieldNames = new HashSet<>(); final List> fieldSuperclassReversedOrder = new ArrayList<>(); TypeResolutions currTypeResolutions = null; for (Type currType = cls; currType != Object.class && currType != null;) { Class currRawType; ParameterizedType currParameterizedType; if (currType instanceof ParameterizedType) { currParameterizedType = (ParameterizedType) currType; currRawType = (Class) currParameterizedType.getRawType(); } else if (currType instanceof Class) { currParameterizedType = null; currRawType = (Class) currType; } else { // Class definitions should not be of type WildcardType or GenericArrayType throw new IllegalArgumentException("Illegal class type: " + currType); } final Field[] fields = currRawType.getDeclaredFields(); final List fieldOrderWithinClass = new ArrayList<>(); for (int i = 0; i < fields.length; i++) { final Field field = fields[i]; // Mask superclass fields if subclass has a field of the same name if (visibleFieldNames.add(field.getName())) { // Check for @Id annotation final boolean isIdField = field.isAnnotationPresent(Id.class); if (isIdField) { if (idField != null) { throw new IllegalArgumentException( "More than one @Id annotation: " + idField.getDeclaringClass() + "." + idField + " ; " + currRawType.getName() + "." + field.getName()); } idField = field; } if (JSONUtils.fieldIsSerializable(field, onlySerializePublicFields)) { // Resolve field type variables, if any, using the current type resolutions. This will // completely resolve some types (in the superclass), if the subclass extends a concrete // version of a generic superclass, but it will only partially resolve variables in // superclasses in general. final Type fieldGenericType = field.getGenericType(); final Type fieldTypePartiallyResolved = currTypeResolutions != null && resolveTypes ? currTypeResolutions.resolveTypeVariables(fieldGenericType) : fieldGenericType; // Save field and its partially resolved type final FieldTypeInfo fieldTypeInfo = new FieldTypeInfo(field, fieldTypePartiallyResolved, classFieldCache); fieldNameToFieldTypeInfo.put(field.getName(), fieldTypeInfo); fieldOrderWithinClass.add(fieldTypeInfo); } else if (isIdField) { throw new IllegalArgumentException( "@Id annotation field must be accessible, final, and non-transient: " + currRawType.getName() + "." + field.getName()); } } } // Save fields group in the order they were defined in the class, but in reverse order of superclasses fieldSuperclassReversedOrder.add(fieldOrderWithinClass); // Move up to superclass, resolving superclass type variables using current class' type resolutions // e.g. if the current resolutions list is { T => Integer }, and the current class is C, all fields // of type T were resolved above to type Integer. If C extends B, then resolve B to B, // and look up B's own generic type to produce the list of resolutions for fields in B (e.g. if B is // defined as B, then after resolving B to B, we can produce a new list of resolutions, // { V => Integer } ). final Type genericSuperType = currRawType.getGenericSuperclass(); if (resolveTypes) { if (genericSuperType instanceof ParameterizedType) { // Resolve TypeVariables in the generic supertype of the class, using the current type resolutions final Type resolvedSupertype = currTypeResolutions == null ? genericSuperType : currTypeResolutions.resolveTypeVariables(genericSuperType); // Produce new type resolutions for the superclass, by comparing its concrete to its generic type currTypeResolutions = resolvedSupertype instanceof ParameterizedType ? new TypeResolutions((ParameterizedType) resolvedSupertype) : null; // Iterate to superclass currType = resolvedSupertype; } else if (genericSuperType instanceof Class) { // In the case of a raw class, the generic supertype may already have resolved type variables, // e.g. "class A extends B" currType = genericSuperType; currTypeResolutions = null; } else { throw new IllegalArgumentException("Got unexpected supertype " + genericSuperType); } } else { // If not resolving types, just move up to supertype currType = genericSuperType; } } // Reverse the order of field visibility, so that ancestral superclass fields appear top-down, in field // definition order (if not masked by same-named fields in subclasses), followed by fields in sublcasses. for (int i = fieldSuperclassReversedOrder.size() - 1; i >= 0; i--) { final List fieldGroupingForClass = fieldSuperclassReversedOrder.get(i); fieldOrder.addAll(fieldGroupingForClass); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy