1 /* 2 * Copyright (c) 2010 Carman Consulting, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.metastopheles; 18 19 import java.beans.BeanInfo; 20 import java.beans.IntrospectionException; 21 import java.beans.Introspector; 22 import java.beans.MethodDescriptor; 23 import java.beans.PropertyDescriptor; 24 import java.lang.reflect.Method; 25 import java.util.HashMap; 26 import java.util.HashSet; 27 import java.util.LinkedList; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.Set; 31 import java.util.UUID; 32 import java.util.WeakHashMap; 33 import java.util.concurrent.ConcurrentMap; 34 35 /** 36 * @author James Carman 37 * @since 1.0 38 */ 39 public class BeanMetaDataFactory 40 { 41 //********************************************************************************************************************** 42 // Fields 43 //********************************************************************************************************************** 44 45 private static final Map<String,BeanMetaDataFactory> factoryRegistry = new HashMap<String,BeanMetaDataFactory>(); 46 private final Map<Class, BeanMetaData> metaDataMap = new WeakHashMap<Class, BeanMetaData>(); 47 48 private List<MetaDataDecorator<BeanMetaData>> beanMetaDataDecorators = new LinkedList<MetaDataDecorator<BeanMetaData>>(); 49 private List<MetaDataDecorator<MethodMetaData>> methodMetaDataDecorators = new LinkedList<MetaDataDecorator<MethodMetaData>>(); 50 private List<MetaDataDecorator<PropertyMetaData>> propertyMetaDataDecorators = new LinkedList<MetaDataDecorator<PropertyMetaData>>(); 51 private final String id = UUID.randomUUID().toString(); 52 53 //********************************************************************************************************************** 54 // Static Methods 55 //********************************************************************************************************************** 56 57 static BeanMetaDataFactory get(String id) 58 { 59 return factoryRegistry.get(id); 60 } 61 62 //********************************************************************************************************************** 63 // Constructors 64 //********************************************************************************************************************** 65 66 public BeanMetaDataFactory() 67 { 68 factoryRegistry.put(id, this); 69 } 70 71 //********************************************************************************************************************** 72 // Getter/Setter Methods 73 //********************************************************************************************************************** 74 75 public List<MetaDataDecorator<BeanMetaData>> getBeanMetaDataDecorators() 76 { 77 return beanMetaDataDecorators; 78 } 79 80 public void setBeanMetaDataDecorators(List<MetaDataDecorator<BeanMetaData>> beanMetaDataDecorators) 81 { 82 this.beanMetaDataDecorators = beanMetaDataDecorators; 83 } 84 85 public List<MetaDataDecorator<MethodMetaData>> getMethodMetaDataDecorators() 86 { 87 return methodMetaDataDecorators; 88 } 89 90 public void setMethodMetaDataDecorators(List<MetaDataDecorator<MethodMetaData>> methodMetaDataDecorators) 91 { 92 this.methodMetaDataDecorators = methodMetaDataDecorators; 93 } 94 95 public List<MetaDataDecorator<PropertyMetaData>> getPropertyMetaDataDecorators() 96 { 97 return propertyMetaDataDecorators; 98 } 99 100 public void setPropertyMetaDataDecorators(List<MetaDataDecorator<PropertyMetaData>> propertyMetaDataDecorators) 101 { 102 this.propertyMetaDataDecorators = propertyMetaDataDecorators; 103 } 104 105 //********************************************************************************************************************** 106 // Other Methods 107 //********************************************************************************************************************** 108 109 public synchronized void clear() 110 { 111 metaDataMap.clear(); 112 } 113 114 public synchronized BeanMetaData getBeanMetaData(Class beanClass) 115 { 116 if (metaDataMap.containsKey(beanClass)) 117 { 118 return metaDataMap.get(beanClass); 119 } 120 else 121 { 122 try 123 { 124 BeanInfo beanInfo = Introspector.getBeanInfo(beanClass); 125 BeanMetaData beanMetaData = new BeanMetaData(id, beanInfo.getBeanDescriptor()); 126 for (MetaDataDecorator<BeanMetaData> decorator : beanMetaDataDecorators) 127 { 128 decorator.decorate(beanMetaData); 129 } 130 final Set<Method> visitedMethods = new HashSet<Method>(); 131 for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) 132 { 133 visitedMethods.add(descriptor.getReadMethod()); 134 visitedMethods.add(descriptor.getWriteMethod()); 135 if (!"class".equals(descriptor.getName())) 136 { 137 final PropertyMetaData propertyMetaData = new PropertyMetaData(beanMetaData, descriptor); 138 beanMetaData.addPropertyMetaData(propertyMetaData); 139 for (MetaDataDecorator<PropertyMetaData> decorator : propertyMetaDataDecorators) 140 { 141 decorator.decorate(propertyMetaData); 142 } 143 } 144 } 145 for (MethodDescriptor descriptor : beanInfo.getMethodDescriptors()) 146 { 147 if (!visitedMethods.contains(descriptor.getMethod()) && !Object.class.equals(descriptor.getMethod().getDeclaringClass())) 148 { 149 final MethodMetaData methodMetaData = new MethodMetaData(beanMetaData, descriptor); 150 beanMetaData.addMethodMetaData(methodMetaData); 151 for (MetaDataDecorator<MethodMetaData> decorator : methodMetaDataDecorators) 152 { 153 decorator.decorate(methodMetaData); 154 } 155 } 156 } 157 metaDataMap.put(beanClass, beanMetaData); 158 return beanMetaData; 159 } 160 catch (IntrospectionException e) 161 { 162 throw new RuntimeException("Unable to lookup bean information for bean class " + beanClass.getName() + ".", e); 163 } 164 } 165 } 166 }