001    /*
002     * Copyright 2011 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 1.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl1.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.student.datadictionary.util;
017    
018    import java.beans.BeanInfo;
019    import java.beans.IntrospectionException;
020    import java.beans.Introspector;
021    import java.beans.PropertyDescriptor;
022    import java.lang.reflect.Field;
023    import java.lang.reflect.ParameterizedType;
024    import java.lang.reflect.Type;
025    import java.lang.reflect.TypeVariable;
026    import java.util.Date;
027    import java.util.LinkedHashSet;
028    import java.util.List;
029    import java.util.Set;
030    
031    public class ComplexSubstructuresHelper {
032    
033        public Set<String> getComplexStructures(String className) {
034            Set<String> complexStructures = new LinkedHashSet<String>();
035            loadComplexStructures(className, complexStructures);
036            return complexStructures;
037        }
038    
039        private void loadComplexStructures(String className,
040                Set<String> complexStructures) {
041            if (!complexStructures.add(className)) {
042                return;
043            }
044            BeanInfo beanInfo;
045            Class<?> clazz;
046            try {
047                clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
048                // had to change the standard Class.forName below to the above so it uses the right class loader
049                // that is defined in KSDictionaryDocMojo.java
050    //            clazz = Class.forName(className);
051            } catch (ClassNotFoundException ex) {
052                System.out.println("ComplexSubstructuresHelper: Could not process because the class must be a freestanding object: " + className);
053                return;
054            }
055            try {
056                beanInfo = Introspector.getBeanInfo(clazz);
057            } catch (IntrospectionException ex) {
058                throw new RuntimeException(ex);
059            }
060            for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
061                Class<?> subClass = pd.getPropertyType();
062                if (List.class.equals(subClass)) {
063                    // recursively check super classes for field if not declared on this class
064                    subClass = getActualClassFromList(clazz, pd.getName());
065                }
066                if (!Class.class.equals(subClass)
067                        && !String.class.equals(subClass)
068                        && !Integer.class.equals(subClass)
069                        && !Long.class.equals(subClass)
070                        && !Boolean.class.equals(subClass)
071                        && !boolean.class.equals(subClass)
072                        && !int.class.equals(subClass)
073                        && !long.class.equals(subClass)
074                        && !Double.class.equals(subClass)
075                        && !Float.class.equals(subClass)
076                        && !Date.class.equals(subClass)
077                        && !Enum.class.isAssignableFrom(subClass)
078                        && !Object.class.equals(subClass)) {
079                    loadComplexStructures(subClass.getName(), complexStructures);
080                }
081            }
082        }
083    
084        public static Class<?> getActualClassFromList(Class<?> originalClass, String fieldName) {
085            if (originalClass.isInterface()) {
086                throw new RuntimeException("Interface used in getter, use xxxInfo instead for field: " + originalClass.getName() + "." + fieldName);
087            }
088            // recursively check super classes for field if not declared on this class
089            Class<?> classToCheck = originalClass;
090            while (true) {
091                try {
092                    Field field = classToCheck.getDeclaredField(fieldName);
093                    Type type = field.getGenericType();
094                    ParameterizedType pt = (ParameterizedType) type;
095                    Type actualType = pt.getActualTypeArguments()[0];
096                    return (Class<?>) actualType;
097                } catch (NoSuchFieldException ex) {
098                    classToCheck = classToCheck.getSuperclass();
099                    if (classToCheck == null) {
100                        throw new RuntimeException("No such field: " + originalClass.getName() + "." + fieldName, ex);
101                    }
102                    if (classToCheck.equals(Object.class)) {
103                        throw new RuntimeException("No such field: " + originalClass.getName() + "." + fieldName, ex);
104                    }
105                } catch (SecurityException ex) {
106                    throw new RuntimeException(originalClass.getName() + "." + fieldName, ex);
107                }
108            }
109        }
110    }