View Javadoc

1   /*
2    * Copyright (C) 2004 TiongHiang Lee
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not,  write to the Free Software
16   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17   *
18   * Email: thlee@onemindsoft.org
19   */
20  
21  package org.onemind.commons.relabean;
22  
23  import java.beans.*;
24  import java.beans.PropertyDescriptor;
25  import java.lang.reflect.Method;
26  import java.util.*;
27  import org.apache.log4j.Logger;
28  /***
29   * A Relational Bean introspector uses a relational bean model descriptor - introspect a root bean 
30   * to generate a RelationalBeanModel - introspect a bean to generate a RelationalBeanInfo based
31   * 
32   * @author TiongHiang Lee (thlee@onemindsoft.org)
33   * @version $Id: RelationalBeanIntrospector.java,v 1.2 2004/08/26 16:28:58 thlee Exp $ $Name:  $
34   */
35  public final class RelationalBeanIntrospector
36  {
37  
38      /*** the logger * */
39      private static final Logger _logger = Logger
40              .getLogger(RelationalBeanIntrospector.class);
41  
42      /***
43       * {@inheritDoc}
44       */
45      private RelationalBeanIntrospector()
46      {
47      }
48  
49      /***
50       * Get an instance of the introspector
51       * 
52       * @return the instance
53       */
54      public static RelationalBeanIntrospector getInstance()
55      {
56          return new RelationalBeanIntrospector();
57      }
58  
59      /***
60       * Introspect the RelationalBeanInfo based on the model descriptor
61       * 
62       * @param desc the model descritpror
63       * @param beanClass the bean class
64       * @return the RelationalBeanInfo
65       * @throws IntrospectionException if there's problem introspect the bean properties
66       */
67      public RelationalBeanInfo introspectBeanInfo(
68              RelationalBeanModelDescriptor desc, Class beanClass)
69              throws IntrospectionException
70      {
71          RelationalBeanInfo relaInfo = new RelationalBeanInfo(beanClass);
72          BeanInfo info = null;
73          info = Introspector
74                  .getBeanInfo(beanClass, desc.getStopClass(beanClass));
75          PropertyDescriptor[] propdesc = info.getPropertyDescriptors();
76          for (int i = 0; i < propdesc.length; i++)
77          {
78              Class propType = propdesc[i].getPropertyType();
79              if (propType != null)
80              {
81                  if (propType.isPrimitive() || desc.isPrimitiveType(propType))
82                  {
83                      relaInfo
84                              .addProperty(new org.onemind.commons.relabean.PropertyDescriptor(
85                                      propdesc[i].getName(), propdesc[i]
86                                              .getReadMethod(), propdesc[i]
87                                              .getWriteMethod()));
88                  } else
89                  {
90                      relaInfo.addRelation(constructRelationDescriptor(beanClass,
91                              propdesc[i]));
92                  }
93              } else
94              {
95                  _logger.warn("bypass field " + propdesc[i].getName() + " of "
96                          + relaInfo.getBeanClass() + " because of null type");
97              }
98          }
99          return relaInfo;
100     }
101 
102     /***
103      * Construct a relation descriptor for a property descriptor for the given bean class
104      * 
105      * @param beanClass the bean class
106      * @param desc the descriptor
107      * @return the relation descriptor
108      * @todo improve the detection of the member type of the composite relation 
109      */
110     private RelationDescriptor constructRelationDescriptor(Class beanClass,
111             PropertyDescriptor desc)
112     {
113         RelationDescriptor rdesc = new RelationDescriptor(desc.getName(), desc
114                 .getPropertyType(), desc.getReadMethod(), desc.getWriteMethod());
115         if (Collection.class.isAssignableFrom(desc.getPropertyType()))
116         {
117             Method addMethod = guessAddMethod(beanClass, desc.getName());
118             rdesc.setAddMethod(addMethod);
119             rdesc.setRemoveMethod(guessRemoveMethod(beanClass, desc.getName()));
120             if ((addMethod != null)
121                     && (addMethod.getParameterTypes().length > 0))
122             {
123                 //TODO: improve detection
124                 rdesc.setMemberType(addMethod.getParameterTypes()[0]);
125             }
126         }
127         return rdesc;
128     }
129 
130     /***
131      * @param beanClass tje bean class
132      * @param relationName the relation
133      * @return the method, or null
134      * @todo optimize the guessing of remove method
135      */
136     private Method guessRemoveMethod(Class beanClass, String relationName)
137     {
138         // TODO: Optimize this
139         Method[] methods = beanClass.getMethods();
140         for (int i = 0; i < methods.length; i++)
141         {
142             if (methods[i].getName().startsWith("remove"))
143             {
144                 String suffix = methods[i].getName().substring(6);
145                 if ((relationName.length() > suffix.length())
146                         && relationName.substring(0, suffix.length())
147                                 .equalsIgnoreCase(suffix))
148                 {
149                     return methods[i];
150                 }
151             }
152         }
153         return null;
154     }
155 
156     /***
157      * Guess the add method of relation
158      * 
159      * @param beanClass the bean class
160      * @param relationName the relation name
161      * @return the add method, or null
162      * @todo optimize the guessing of add method
163      */
164     private Method guessAddMethod(Class beanClass, String relationName)
165     {
166         // TODO: Optimize this
167         Method[] methods = beanClass.getMethods();
168         for (int i = 0; i < methods.length; i++)
169         {
170             if (methods[i].getName().startsWith("add"))
171             {
172                 String suffix = methods[i].getName().substring(3);
173                 if ((relationName.length() > suffix.length())
174                         && relationName.substring(0, suffix.length())
175                                 .equalsIgnoreCase(suffix))
176                 {
177                     return methods[i];
178                 }
179             }
180         }
181         return null;
182     }
183 
184     /***
185      * Construct the relational bean model from a set of bean classes using the relational bean model descriptor
186      * 
187      * @param desc the descriptor
188      * @param beanClasses the bean classes
189      * @return the relation bean model
190      * @throws IntrospectionException if there's problem in instropecting the bean properties
191      */
192     public RelationalBeanModel introspectModel(
193             RelationalBeanModelDescriptor desc, Collection beanClasses)
194             throws IntrospectionException
195     {
196         RelationalBeanModel model = new RelationalBeanModel(desc);
197         Iterator it = beanClasses.iterator();
198         while (it.hasNext())
199         {
200             model.addBeanInfo(introspectBeanInfo(desc, (Class) it.next()));
201         }
202         return model;
203     }
204 
205     /***
206      * Return a relational bean model from the given set of beans based on reflections
207      * 
208      * @param list the list of beans
209      * @return the relational bean model
210      * @throws IntrospectionException if there's exception introspecting the model
211      */
212     public RelationalBeanModel introspectModel(List list)
213             throws IntrospectionException
214     {
215         return introspectModel(new RelationalBeanModelDescriptor("Unnamed"),
216                 list);
217     }
218 }