001package org.javasimon.console.reflect;
002
003import org.javasimon.console.SimonTypeFactory;
004
005import java.io.IOException;
006import java.io.InputStream;
007import java.lang.reflect.Method;
008import java.lang.reflect.Modifier;
009import java.util.Collection;
010import java.util.HashMap;
011import java.util.Map;
012import java.util.Properties;
013
014/**
015 * {@link Getter} factory.
016 * Contains a cache Class → Property Name → Getter
017 *
018 * @author gquintana
019 */
020public class GetterFactory {
021        private static final Map<Class, Map<String, Getter>> GETTER_CACHE = new HashMap<>();
022
023        /** Test whether method is a getter. */
024        private static boolean isGetterMethod(Method method) {
025                return !Modifier.isStatic(method.getModifiers())
026                        && (method.getName().startsWith("get") || method.getName().startsWith("is"))
027                        && method.getParameterTypes().length == 0;
028        }
029
030        /** Transform method name into property name. */
031        private static String getPropertyName(Method method) {
032                String propertyName = method.getName();
033                if (propertyName.startsWith("get")) {
034                        propertyName = propertyName.substring(3);
035                } else if (propertyName.startsWith("is")) {
036                        propertyName = propertyName.substring(2);
037                }
038                propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
039                return propertyName;
040        }
041
042        /** Constrains the content of the SubTypes.properties file. */
043        private static final Properties subTypeProperties = new Properties();
044
045        static {
046                try {
047                        InputStream inputStream = GetterFactory.class.getResourceAsStream("SubTypes.properties");
048                        subTypeProperties.load(inputStream);
049                } catch (IOException iOException) {
050                        iOException.printStackTrace();
051                }
052        }
053
054        /**
055         * Get subtype for given Class and property.
056         *
057         * @param type Parent class
058         * @param propertyName Property name
059         * @return Sub type name
060         */
061        private static String getSubType(Class type, String propertyName) {
062                @SuppressWarnings("unchecked")
063                Class normalizedType = SimonTypeFactory.normalizeType(type);
064                if (normalizedType == null) {
065                        return null;
066                } else {
067                        return subTypeProperties.getProperty(normalizedType.getName() + "." + propertyName);
068                }
069        }
070
071        /**
072         * Extract all getters for given class.
073         *
074         * @param type Class
075         * @return List of getters
076         */
077        @SuppressWarnings("unchecked")
078        public static Collection<Getter> getGetters(Class type) {
079                return getGettersAsMap(type).values();
080        }
081
082        /**
083         * Extract all getters for given class.
084         *
085         * @param type Class
086         * @return Map property name &rarr; Getter
087         */
088        private static Map<String, Getter> getGettersAsMap(Class type) {
089                Map<String, Getter> typeGetters = GETTER_CACHE.get(type);
090                if (typeGetters == null) {
091                        typeGetters = new HashMap<>();
092                        for (Method method : type.getMethods()) {
093                                if (isGetterMethod(method)) {
094                                        String propertyName = getPropertyName(method);
095                                        Class propertyType = method.getReturnType();
096                                        //noinspection unchecked
097                                        typeGetters.put(propertyName, new Getter(propertyName, propertyType, getSubType(type, propertyName), method));
098                                }
099                        }
100                        GETTER_CACHE.put(type, typeGetters);
101                }
102                return typeGetters;
103        }
104
105        /**
106         * Search getter for given class and property name.
107         *
108         * @param type Class
109         * @param name Property name
110         * @return Getter or null if not found
111         */
112        @SuppressWarnings("unchecked")
113        public static Getter getGetter(Class type, String name) {
114                return getGettersAsMap(type).get(name);
115        }
116}