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

com.swirlds.config.extensions.reflection.ConfigReflectionUtils Maven / Gradle / Ivy

Go to download

Swirlds is a software platform designed to build fully-distributed applications that harness the power of the cloud without servers. Now you can develop applications with fairness in decision making, speed, trust and reliability, at a fraction of the cost of traditional server-based platforms.

There is a newer version: 0.56.6
Show newest version
/*
 * Copyright (C) 2022-2024 Hedera Hashgraph, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.swirlds.config.extensions.reflection;

import com.swirlds.config.api.ConfigData;
import com.swirlds.config.api.ConfigProperty;
import com.swirlds.config.api.Configuration;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.RecordComponent;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Some methods that are needed for the initialization of the config that internally use reflection.
 */
public final class ConfigReflectionUtils {

    private ConfigReflectionUtils() {}

    /**
     * Returns the generic type of the class or throws an {@link IllegalArgumentException} if the given class has not
     * exactly one generic type.
     *
     * @param parameterizedType the class
     * @return the generic type of the class
     */
    public static Type getSingleGenericTypeArgument(final ParameterizedType parameterizedType) {
        if (parameterizedType.getActualTypeArguments().length != 1) {
            throw new IllegalArgumentException("Only exactly 1 generic type is supported");
        }
        return parameterizedType.getActualTypeArguments()[0];
    }

    /**
     * Returns true if the given class is public.
     *
     * @param type the class
     * @return true if the given class is public
     */
    public static boolean isPublic(final Class type) {
        return Modifier.isPublic(type.getModifiers());
    }

    /**
     * Returns the config property name for a property of a config data object (see {@link ConfigData}).
     *
     * @param prefix    the prefix of the  config data type
     * @param component the record component thatd efines the property
     * @return the config property name for a property
     */
    public static String getPropertyNameForConfigDataProperty(final String prefix, final RecordComponent component) {
        return Optional.ofNullable(component.getAnnotation(ConfigProperty.class))
                .map(propertyAnnotation -> {
                    if (!propertyAnnotation.value().isBlank()) {
                        return getPropertyNameForConfigDataProperty(prefix, propertyAnnotation.value());
                    } else {
                        return getPropertyNameForConfigDataProperty(prefix, component.getName());
                    }
                })
                .orElseGet(() -> getPropertyNameForConfigDataProperty(prefix, component.getName()));
    }

    /**
     * Returns the config property name for a property of a config data object (see {@link ConfigData}).
     *
     * @param prefix the prefix of the  config data type
     * @param name   the name of the property
     * @return the config property name
     */
    public static String getPropertyNameForConfigDataProperty(final String prefix, final String name) {
        if (prefix.isBlank()) {
            return name;
        }
        return prefix + "." + name;
    }

    /**
     * Returns the name of a config data type (see {@link ConfigData}).
     *
     * @param type the config data type
     * @return the name of a config data type
     */
    public static String getNamePrefixForConfigDataRecord(final AnnotatedElement type) {
        return Optional.ofNullable(type.getAnnotation(ConfigData.class))
                .map(ConfigData::value)
                .orElse("");
    }

    /**
     * Returns all {@link AnnotatedProperty} that can be found for the given constraint annotation.
     *
     * @param constraintAnnotationType the type of the constraint annotation
     * @param configuration            the configuration that should be used for the search
     * @param                       the annotation type
     * @param                       the type of possible values
     * @return all {@link AnnotatedProperty} that can be found for the given constraint annotation
     */
    public static 
            List> getAllMatchingPropertiesForConstraintAnnotation(
                    final Class constraintAnnotationType, final Configuration configuration) {
        Objects.requireNonNull(constraintAnnotationType, "annotationType can not be null");
        Objects.requireNonNull(configuration, "configuration can not be null");
        return configuration.getConfigDataTypes().stream()
                .flatMap(recordType -> Arrays.stream(recordType.getRecordComponents()))
                .filter(component -> component.isAnnotationPresent(constraintAnnotationType))
                .map(component ->
                        (AnnotatedProperty) createData(constraintAnnotationType, configuration, component))
                .collect(Collectors.toList());
    }

    /**
     * Creates a {@link AnnotatedProperty} for the given values.
     *
     * @param annotationType the type of the annotation
     * @param configuration  the configuration
     * @param component      the component
     * @param             type of the annotation
     * @param             type of the value
     * @return the AnnotatedProperty
     */
    private static  AnnotatedProperty createData(
            final Class annotationType, final Configuration configuration, final RecordComponent component) {
        try {
            final A annotation = component.getAnnotation(annotationType);
            final Class recordType = (Class) component.getDeclaringRecord();
            final String propertyNamePrefix = getNamePrefixForConfigDataRecord(recordType);
            final Object recordInstance = configuration.getConfigData(recordType);
            final V propertyValue;
            propertyValue = (V) component.getAccessor().invoke(recordInstance);

            final String propertyName = getPropertyNameForConfigDataProperty(propertyNamePrefix, component);
            final Class propertyType = (Class) component.getType();
            return new AnnotatedProperty<>(annotation, component, propertyName, propertyValue, propertyType);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new IllegalArgumentException("Can not get the needed metadata for the given type", e);
        }
    }

    public record AnnotatedProperty(
            A annotation, RecordComponent component, String propertyName, V propertyValue, Class propertyType) {}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy