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    import org.kuali.student.contract.model.impl.ServiceContractModelPescXsdLoader;
032    import org.slf4j.Logger;
033    import org.slf4j.LoggerFactory;
034    
035    public class ComplexSubstructuresHelper {
036        
037        private static Logger log = LoggerFactory.getLogger(ComplexSubstructuresHelper.class);
038        
039    
040        public Set<String> getComplexStructures(String className) {
041            Set<String> complexStructures = new LinkedHashSet<String>();
042            loadComplexStructures(className, complexStructures);
043            return complexStructures;
044        }
045    
046        private void loadComplexStructures(String className,
047                Set<String> complexStructures) {
048            if (!complexStructures.add(className)) {
049                return;
050            }
051            BeanInfo beanInfo;
052            Class<?> clazz;
053            try {
054                clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
055                // had to change the standard Class.forName below to the above so it uses the right class loader
056                // that is defined in KSDictionaryDocMojo.java
057    //            clazz = Class.forName(className);
058            } catch (ClassNotFoundException ex) {
059                log.warn("ComplexSubstructuresHelper: Could not process because the class must be a freestanding object: " + className);
060                return;
061            }
062            try {
063                beanInfo = Introspector.getBeanInfo(clazz);
064            } catch (IntrospectionException ex) {
065                throw new RuntimeException(ex);
066            }
067            for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
068                Class<?> subClass = pd.getPropertyType();
069                if (List.class.equals(subClass)) {
070                    // recursively check super classes for field if not declared on this class
071                    subClass = getActualClassFromList(clazz, pd.getName());
072                }
073                if (!Class.class.equals(subClass)
074                        && !String.class.equals(subClass)
075                        && !Integer.class.equals(subClass)
076                        && !Long.class.equals(subClass)
077                        && !Boolean.class.equals(subClass)
078                        && !boolean.class.equals(subClass)
079                        && !int.class.equals(subClass)
080                        && !long.class.equals(subClass)
081                        && !Double.class.equals(subClass)
082                        && !Float.class.equals(subClass)
083                        && !Date.class.equals(subClass)
084                        && !Enum.class.isAssignableFrom(subClass)
085                        && !Object.class.equals(subClass)) {
086                    loadComplexStructures(subClass.getName(), complexStructures);
087                }
088            }
089        }
090    
091        public static Class<?> getActualClassFromList(Class<?> originalClass, String fieldName) {
092            if (originalClass.isInterface()) {
093                throw new RuntimeException("Interface used in getter, use xxxInfo instead for field: " + originalClass.getName() + "." + fieldName);
094            }
095            // recursively check super classes for field if not declared on this class
096            Class<?> classToCheck = originalClass;
097            while (true) {
098                try {
099                    Field field = classToCheck.getDeclaredField(fieldName);
100                    Type type = field.getGenericType();
101                    ParameterizedType pt = (ParameterizedType) type;
102                    Type actualType = pt.getActualTypeArguments()[0];
103                    return (Class<?>) actualType;
104                } catch (NoSuchFieldException ex) {
105                    classToCheck = classToCheck.getSuperclass();
106                    if (classToCheck == null) {
107                        throw new RuntimeException("No such field: " + originalClass.getName() + "." + fieldName, ex);
108                    }
109                    if (classToCheck.equals(Object.class)) {
110                        throw new RuntimeException("No such field: " + originalClass.getName() + "." + fieldName, ex);
111                    }
112                } catch (SecurityException ex) {
113                    throw new RuntimeException(originalClass.getName() + "." + fieldName, ex);
114                }
115            }
116        }
117    }