001/**
002 * Copyright 2004-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.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/ecl2.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 */
016package org.kuali.student.datadictionary.util;
017
018import java.beans.BeanInfo;
019import java.beans.IntrospectionException;
020import java.beans.Introspector;
021import java.beans.PropertyDescriptor;
022import java.lang.reflect.Field;
023import java.lang.reflect.ParameterizedType;
024import java.lang.reflect.Type;
025import java.lang.reflect.TypeVariable;
026import java.util.Date;
027import java.util.LinkedHashSet;
028import java.util.List;
029import java.util.Set;
030
031import org.kuali.student.contract.model.impl.ServiceContractModelPescXsdLoader;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035public 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}