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 }