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

org.dizitart.no2.util.ReflectionUtils Maven / Gradle / Ivy

There is a newer version: 4.3.0
Show newest version
/*
 *
 * Copyright 2017-2018 Nitrite author or authors.
 *
 * 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 org.dizitart.no2.util;

import lombok.experimental.UtilityClass;
import org.dizitart.no2.exceptions.ValidationException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import static org.dizitart.no2.exceptions.ErrorCodes.*;
import static org.dizitart.no2.exceptions.ErrorMessage.OBJ_INVALID_EMBEDDED_FIELD;
import static org.dizitart.no2.exceptions.ErrorMessage.errorMessage;
import static org.dizitart.no2.util.DocumentUtils.FIELD_SEPARATOR;
import static org.dizitart.no2.util.StringUtils.isNullOrEmpty;
import static org.dizitart.no2.util.ValidationUtils.notNull;

/**
 * A utility class for reflection.
 *
 * @since 1.0
 * @author Anindya Chatterjee
 */
@UtilityClass
public class ReflectionUtils {

    /**
     * Gets the class hierarchy.
     *
     * @param startClass      the start class
     * @param exclusiveParent the exclusive parent
     * @return all declared fields in the specified class hierarchy.
     */
    public static List getFieldsUpto(Class startClass, Class exclusiveParent) {
        notNull(startClass, errorMessage("startClass can not be null", VE_REFLECT_FIELD_NULL_START_CLASS));
        List currentClassFields = new ArrayList<>(Arrays.asList(startClass.getDeclaredFields()));
        filterSynthetics(currentClassFields);
        Class parentClass = startClass.getSuperclass();

        if (parentClass != null && !(parentClass.equals(exclusiveParent))) {
            List parentClassFields = getFieldsUpto(parentClass, exclusiveParent);
            currentClassFields.addAll(parentClassFields);
        }

        return currentClassFields;
    }

    static  List findAnnotations(Class annotation, Class type) {
        notNull(type, errorMessage("type can not be null", VE_REFLECT_NULL_START_CLASS));
        notNull(annotation, errorMessage("annotationClass can not be null", VE_REFLECT_NULL_ANNOT_CLASS));
        List annotations = new ArrayList<>();

        T t = type.getAnnotation(annotation);
        if (t != null) annotations.add(t);

        Class[] interfaces = type.getInterfaces();
        for (Class anInterface : interfaces) {
            T ann = anInterface.getAnnotation(annotation);
            if (ann != null) annotations.add(ann);
        }

        Class parentClass = type.getSuperclass();
        if (parentClass != null && !parentClass.equals(Object.class)) {
            List list = findAnnotations(annotation, parentClass);
            annotations.addAll(list);
        }

        return annotations;
    }


    /**
     * Gets all annotated fields in the entire class hierarchy.
     *
     * @param startClass      the start class in the class hierarchy.
     * @param annotationClass the annotation class
     * @return the annotated fields
     */
    static List getAnnotatedFields(Class startClass, Class annotationClass) {
        notNull(startClass, errorMessage("startClass can not be null", VE_REFLECT_NULL_START_CLASS));
        notNull(annotationClass, errorMessage("annotationClass can not be null", VE_REFLECT_NULL_ANNOT_CLASS));
        Iterable fields = getFieldsUpto(startClass, Object.class);
        List filtered = new ArrayList<>();
        for (Field field : fields) {
            Object annotation = field.getAnnotation(annotationClass);
            if (annotation != null) {
                filtered.add(field);
            }
        }
        return filtered;
    }

    static  Field getField(Class type, String name, boolean recursive) {
        if (name.contains(FIELD_SEPARATOR)) {
            return getEmbeddedField(type, name);
        } else {
            // first check declared fields (fix for kotlin properties, ref: issue #54)
            // if nothing found and is-recursive then check recursively
            Field[] declaredFields = type.getDeclaredFields();
            Field field = null;
            for (Field declaredField : declaredFields) {
                if (declaredField.getName().equals(name)) {
                    field = declaredField;
                    break;
                }
            }

            if (field == null && recursive) {
                List fields = getFieldsUpto(type, Object.class);
                for (Field recursiveField : fields) {
                    if (recursiveField.getName().equals(name)) {
                        field = recursiveField;
                        break;
                    }
                }
            }
            if (field == null) {
                throw new ValidationException(errorMessage(
                        "no such value \'" + name + "\' for type " + type.getName(),
                        VE_REFLECT_FIELD_NO_SUCH_FIELD));
            }
            return field;
        }
    }

    private static void filterSynthetics(List fields) {
        if (fields == null || fields.isEmpty()) return;
        Iterator iterator = fields.iterator();
        while (iterator.hasNext()) {
            Field f = iterator.next();
            if (f.isSynthetic()) iterator.remove();
        }
    }

    private static  Field getEmbeddedField(Class startingClass, String embeddedField) {
        String regex = "\\" + FIELD_SEPARATOR;
        String[] split = embeddedField.split(regex, 2);
        String key = split[0];
        String remaining = split.length == 2 ? split[1] : "";

        if (isNullOrEmpty(key)) {
            throw new ValidationException(OBJ_INVALID_EMBEDDED_FIELD);
        }

        Field field;
        try {
            field = startingClass.getDeclaredField(key);
        } catch (NoSuchFieldException nsfe) {
            throw new ValidationException(errorMessage(
                    "no such value \'" + key + "\' for type " + startingClass.getName(),
                    VE_OBJ_INVALID_FIELD));
        }

        if (!isNullOrEmpty(remaining) || remaining.contains(FIELD_SEPARATOR)) {
            return getEmbeddedField(field.getType(), remaining);
        } else {
            return field;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy