com.astamuse.asta4d.util.annotation.AnnotatedPropertyUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.astamuse.asta4d.util.annotation.AnnotatedPropertyUtil.java

Source

/*
 * Copyright 2014 astamuse company,Ltd.
 * 
 * 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.astamuse.asta4d.util.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.astamuse.asta4d.Configuration;
import com.astamuse.asta4d.util.ClassUtil;

public class AnnotatedPropertyUtil {

    private static class ReadOnlyAnnotatedPropertyInfo extends AnnotatedPropertyInfo {
        private AnnotatedPropertyInfo info;

        ReadOnlyAnnotatedPropertyInfo(AnnotatedPropertyInfo info) {
            this.info = info;
        }

        public String getName() {
            return info.getName();
        }

        public void setName(String name) {
            throw new UnsupportedOperationException();
        }

        public String getBeanPropertyName() {
            return info.getBeanPropertyName();
        }

        public void setBeanPropertyName(String beanPropertyName) {
            throw new UnsupportedOperationException();
        }

        public Field getField() {
            return info.getField();
        }

        public void setField(Field field) {
            throw new UnsupportedOperationException();
        }

        public Method getGetter() {
            return info.getGetter();
        }

        public void setGetter(Method getter) {
            throw new UnsupportedOperationException();
        }

        public Method getSetter() {
            return info.getSetter();
        }

        public void setSetter(Method setter) {
            throw new UnsupportedOperationException();
        }

        @SuppressWarnings("rawtypes")
        public Class getType() {
            return info.getType();
        }

        @SuppressWarnings("rawtypes")
        public void setType(Class type) {
            throw new UnsupportedOperationException();
        }

        public <A extends Annotation> A getAnnotation(Class<A> annotationCls) {
            return info.getAnnotation(annotationCls);
        }

        public void setAnnotations(List<Annotation> annotationList) {
            throw new UnsupportedOperationException();
        }

        public void assignValue(Object instance, Object value)
                throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            info.assignValue(instance, value);
        }

        public Object retrieveValue(Object instance)
                throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            return info.retrieveValue(instance);
        }

        public int hashCode() {
            return info.hashCode();
        }

        public boolean equals(Object obj) {
            return info.equals(obj);
        }

        public String toString() {
            return info.toString();
        }

    }

    private static class AnnotatedPropertyInfoMap {
        Map<String, List<AnnotatedPropertyInfo>> nameMap;
        Map<String, List<AnnotatedPropertyInfo>> beanNameMap;
        List<AnnotatedPropertyInfo> list;

        AnnotatedPropertyInfoMap(List<AnnotatedPropertyInfo> infoList) {
            list = infoList.stream().map(info -> new ReadOnlyAnnotatedPropertyInfo(info))
                    .collect(Collectors.toList());
            list = Collections.unmodifiableList(list);

            nameMap = list.stream().collect(Collectors.groupingBy(info -> info.getName()));
            makeListUnmodifiable(nameMap);
            nameMap = Collections.unmodifiableMap(nameMap);

            beanNameMap = list.stream().collect(Collectors.groupingBy(info -> info.getBeanPropertyName()));
            makeListUnmodifiable(beanNameMap);
            beanNameMap = Collections.unmodifiableMap(beanNameMap);
        }

        void makeListUnmodifiable(Map<String, List<AnnotatedPropertyInfo>> map) {
            for (String key : map.keySet()) {
                map.put(key, Collections.unmodifiableList(map.get(key)));
            }
        }

    }

    private static final Logger logger = LoggerFactory.getLogger(AnnotatedPropertyUtil.class);

    private static final ConcurrentHashMap<String, AnnotatedPropertyInfoMap> propertiesMapCache = new ConcurrentHashMap<>();

    // TODO allow method property to override field property to avoid duplicated properties
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private static AnnotatedPropertyInfoMap retrievePropertiesMap(Class cls) {
        String cacheKey = cls.getName();
        AnnotatedPropertyInfoMap map = propertiesMapCache.get(cacheKey);
        if (map == null) {
            List<AnnotatedPropertyInfo> infoList = new LinkedList<>();
            Set<String> beanPropertyNameSet = new HashSet<>();

            Method[] mtds = cls.getMethods();
            for (Method method : mtds) {
                List<Annotation> annoList = ConvertableAnnotationRetriever
                        .retrieveAnnotationHierarchyList(AnnotatedProperty.class, method.getAnnotations());

                if (CollectionUtils.isEmpty(annoList)) {
                    continue;
                }

                AnnotatedPropertyInfo info = new AnnotatedPropertyInfo();
                info.setAnnotations(annoList);

                boolean isGet = false;
                boolean isSet = false;
                String propertySuffixe = method.getName();
                if (propertySuffixe.startsWith("set")) {
                    propertySuffixe = propertySuffixe.substring(3);
                    isSet = true;
                } else if (propertySuffixe.startsWith("get")) {
                    propertySuffixe = propertySuffixe.substring(3);
                    isGet = true;
                } else if (propertySuffixe.startsWith("is")) {
                    propertySuffixe = propertySuffixe.substring(2);
                    isSet = true;
                } else {
                    String msg = String.format("Method [%s]:[%s] can not be treated as a getter or setter method.",
                            cls.getName(), method.toGenericString());
                    throw new RuntimeException(msg);
                }

                char[] cs = propertySuffixe.toCharArray();
                cs[0] = Character.toLowerCase(cs[0]);
                info.setBeanPropertyName(new String(cs));

                AnnotatedProperty ap = (AnnotatedProperty) annoList.get(0);// must by
                String name = ap.name();
                if (StringUtils.isEmpty(name)) {
                    name = info.getBeanPropertyName();
                }

                info.setName(name);

                if (isGet) {
                    info.setGetter(method);
                    info.setType(method.getReturnType());
                    String setterName = "set" + propertySuffixe;
                    Method setter = null;
                    try {
                        setter = cls.getMethod(setterName, method.getReturnType());
                    } catch (NoSuchMethodException | SecurityException e) {
                        String msg = "Could not find setter method:[{}({})] in class[{}] for annotated getter:[{}]";
                        logger.warn(msg, new Object[] { setterName, method.getReturnType().getName(), cls.getName(),
                                method.getName() });
                    }
                    info.setSetter(setter);
                }

                if (isSet) {
                    info.setSetter(method);
                    info.setType(method.getParameterTypes()[0]);
                    String getterName = "get" + propertySuffixe;
                    Method getter = null;
                    try {
                        getter = cls.getMethod(getterName);
                    } catch (NoSuchMethodException | SecurityException e) {
                        String msg = "Could not find getter method:[{}:{}] in class[{}] for annotated setter:[{}]";
                        logger.warn(msg, new Object[] { getterName, method.getReturnType().getName(), cls.getName(),
                                method.getName() });
                    }
                    info.setGetter(getter);
                }

                infoList.add(info);
                beanPropertyNameSet.add(info.getBeanPropertyName());
            }

            List<Field> list = new ArrayList<>(ClassUtil.retrieveAllFieldsIncludeAllSuperClasses(cls));
            Iterator<Field> it = list.iterator();

            while (it.hasNext()) {
                Field f = it.next();
                List<Annotation> annoList = ConvertableAnnotationRetriever
                        .retrieveAnnotationHierarchyList(AnnotatedProperty.class, f.getAnnotations());
                if (CollectionUtils.isNotEmpty(annoList)) {
                    AnnotatedProperty ap = (AnnotatedProperty) annoList.get(0);// must by

                    String beanPropertyName = f.getName();
                    if (beanPropertyNameSet.contains(beanPropertyName)) {
                        continue;
                    }

                    String name = ap.name();
                    if (StringUtils.isEmpty(name)) {
                        name = f.getName();
                    }

                    AnnotatedPropertyInfo info = new AnnotatedPropertyInfo();
                    info.setAnnotations(annoList);
                    info.setBeanPropertyName(beanPropertyName);
                    info.setName(name);
                    info.setField(f);
                    info.setGetter(null);
                    info.setSetter(null);
                    info.setType(f.getType());
                    infoList.add(info);
                }
            }

            map = new AnnotatedPropertyInfoMap(infoList);
            if (Configuration.getConfiguration().isCacheEnable()) {
                propertiesMapCache.put(cacheKey, map);
            }
        }
        return map;
    }

    @SuppressWarnings("rawtypes")
    public static List<AnnotatedPropertyInfo> retrieveProperties(Class cls) {
        AnnotatedPropertyInfoMap map = retrievePropertiesMap(cls);
        return map.list;
    }

    @SuppressWarnings("rawtypes")
    public static List<AnnotatedPropertyInfo> retrievePropertyByName(Class cls, final String name) {
        AnnotatedPropertyInfoMap map = retrievePropertiesMap(cls);
        return map.nameMap.get(name);
    }

    @SuppressWarnings("rawtypes")
    public static List<AnnotatedPropertyInfo> retrievePropertyByBeanPropertyName(Class cls, final String name) {
        AnnotatedPropertyInfoMap map = retrievePropertiesMap(cls);
        return map.beanNameMap.get(name);
    }

    public static void assignValueByName(Object instance, String name, Object value)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        List<AnnotatedPropertyInfo> list = retrievePropertyByName(instance.getClass(), name);
        assignValue(list, instance, value);
    }

    public static void assignValueByBeanPropertyName(Object instance, String name, Object value)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        List<AnnotatedPropertyInfo> list = retrievePropertyByBeanPropertyName(instance.getClass(), name);
        assignValue(list, instance, value);
    }

    public static void assignValue(List<AnnotatedPropertyInfo> list, Object instance, Object value)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        for (AnnotatedPropertyInfo p : list) {
            p.assignValue(instance, value);
        }
    }
}