View Javadoc

1   /*
2    * Copyright 2002-2004 The Apache Software Foundation
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  package org.apache.commons.clazz.bean;
17  
18  import java.lang.reflect.Constructor;
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.apache.commons.clazz.Clazz;
29  import org.apache.commons.clazz.ClazzChangeListener;
30  import org.apache.commons.clazz.ClazzInstanceFactory;
31  import org.apache.commons.clazz.ClazzLoader;
32  import org.apache.commons.clazz.ClazzOperation;
33  import org.apache.commons.clazz.ClazzProperty;
34  
35  /**
36   * Dynamically constructed Clazz. BeanClazzes are created by invoking {@link
37   * ClazzLoader#defineClazz(String, Class, Class) clazzLoader.defineClazz (name,
38   * clazzClass, instanceClass)}.
39   * 
40   * @author <a href="mailto:scolebourne@apache.org">Stephen Colebourne</a>
41   * @author <a href="mailto:dmitri@apache.org">Dmitri Plotnikov</a>
42   * @version $Id: BeanClazz.java 155436 2005-02-26 13:17:48Z dirkv $
43   */
44  public class BeanClazz extends Clazz {
45      private Clazz superClazz;
46      private List declaredProperties = new ArrayList();
47      private List properties = new ArrayList();
48      private Map propertyMap = new HashMap();
49      private List declaredOperations = new ArrayList();
50      private List operations = new ArrayList();
51      private Map operationMap = new HashMap();
52      private List instanceFactories;
53      private Map instanceFactoryMap = new HashMap();
54      private Class instanceClass;
55      private List subclasses = new ArrayList();
56  
57      /**
58       * Constructor for BeanClazz.
59       * @param loader
60       * @param name
61       * @param instanceClass
62       */
63      public BeanClazz(ClazzLoader loader, String name, Class instanceClass) {
64          super(loader, name);
65          this.instanceClass = instanceClass;
66      }
67  
68      public Class getInstanceClass() {
69          if (instanceClass != null) {
70              return instanceClass;
71          }
72  
73          if (superClazz instanceof BeanClazz) {
74              return ((BeanClazz) superClazz).getInstanceClass();
75          }
76  
77          return BasicBean.class;
78      }
79  
80      /**
81       * @see org.apache.commons.clazz.Clazz#getSuperclazz()
82       */
83      public Clazz getSuperclazz() {
84          return superClazz;
85      }
86  
87      public void setSuperclazz(Clazz clazz) {
88          if (superClazz != null) {
89              superClazz.removeClazzChangeListener(listener);
90          }
91          superClazz = clazz;
92          if (clazz != null) {
93              superClazz.addClazzChangeListener(listener);
94          }
95          refreshAllCaches();
96      }
97  
98      protected void refreshAllCaches() {
99          refreshPropertyCache();
100         refreshOperationCache();
101     }
102 
103     protected void refreshPropertyCache() {
104         properties = new ArrayList();
105         propertyMap = new HashMap();
106 
107         Set propertyNames = new HashSet();
108         if (superClazz != null) {
109             List superProperties = superClazz.getProperties();
110             properties.addAll(superProperties);
111             for (int i = 0; i < superProperties.size(); i++) {
112                 ClazzProperty superProperty =
113                     (ClazzProperty) superProperties.get(i);
114                 propertyNames.add(superProperty.getName());
115             }
116         }
117         for (int i = 0; i < declaredProperties.size(); i++) {
118             ClazzProperty property = (ClazzProperty) declaredProperties.get(i);
119             String name = property.getName();
120             if (!propertyNames.contains(name)) {
121                 properties.add(property);
122             }
123         }
124 
125         for (Iterator iter = properties.iterator(); iter.hasNext();) {
126             ClazzProperty property = (ClazzProperty) iter.next();
127             propertyMap.put(property.getName(), property);
128         }
129     }
130 
131     protected void refreshOperationCache() {
132         List operations = new ArrayList();
133         Set signatures = new HashSet();
134         if (superClazz != null) {
135             List superOperations = superClazz.getOperations();
136             operations.addAll(superOperations);
137             for (int i = 0; i < superOperations.size(); i++) {
138                 ClazzOperation superOperation =
139                     (ClazzOperation) superOperations.get(i);
140                 signatures.add(superOperation.getSignature());
141             }
142         }
143         for (int i = 0; i < declaredOperations.size(); i++) {
144             ClazzOperation operation =
145                 (ClazzOperation) declaredOperations.get(i);
146             String signature = operation.getSignature();
147             if (!signatures.contains(signature)) {
148                 operations.add(operation);
149             }
150         }
151     }
152 
153     /**
154      * @see org.apache.commons.clazz.Clazz#getDeclaredProperties()
155      */
156     public List getDeclaredProperties() {
157         return Collections.unmodifiableList(declaredProperties);
158     }
159 
160     public void addDeclaredProperty(ClazzProperty property) {
161         if (property.getDeclaringClazz() != this) {
162             throw new BeanClazzConfigurationException(
163                 "Property belongs to a different clazz: "
164                     + property.getDeclaringClazz().getName());
165         }
166 
167         ClazzProperty oldProperty =
168             (ClazzProperty) propertyMap.get(property.getName());
169         if (oldProperty != null) {
170             removeDeclaredProperty(oldProperty);
171         }
172 
173         declaredProperties.add(property);
174 
175         addProperty(property);
176     }
177 
178     /**
179      * Called indirectly when declared properties are manipulated.
180      */
181     protected void addProperty(ClazzProperty property) {
182         properties.add(property);
183         propertyMap.put(property.getName(), property);
184         firePropertyAdded(property);
185     }
186 
187     public void removeDeclaredProperty(ClazzProperty property) {
188         String name = property.getName();
189         property = (ClazzProperty) propertyMap.get(name);
190         if (property != null) {
191             declaredProperties.remove(property);
192             removeProperty(property);
193         }
194     }
195 
196     /**
197      * Called indirectly when declared properties are manipulated.
198      */
199     protected void removeProperty(ClazzProperty property) {
200         String name = property.getName();
201         properties.remove(property);
202         propertyMap.remove(name);
203         firePropertyRemoved(property);
204 
205         // By deleting this declared property, we may have exposed 
206         // an inherited one
207         if (superClazz != null) {
208             property = superClazz.getProperty(name);
209             if (property != null) {
210                 addProperty(property);
211             }
212         }
213     }
214 
215     /**
216      * @see org.apache.commons.clazz.Clazz#getProperties()
217      */
218     public List getProperties() {
219         return properties;
220     }
221 
222     /**
223      * @see org.apache.commons.clazz.Clazz#getProperty(java.lang.String)
224      */
225     public ClazzProperty getProperty(String name) {
226         return (ClazzProperty) propertyMap.get(name);
227     }
228 
229     /**
230      * @see org.apache.commons.clazz.Clazz#getDeclaredOperations()
231      */
232     public List getDeclaredOperations() {
233         return Collections.unmodifiableList(declaredOperations);
234     }
235 
236     public void addDeclaredOperation(ClazzOperation operation) {
237         if (operation.getDeclaringClazz() != this) {
238             throw new BeanClazzConfigurationException(
239                 "Operation belongs to a different clazz: "
240                     + operation.getDeclaringClazz().getName());
241         }
242 
243         ClazzOperation oldOperation =
244             (ClazzOperation) operationMap.get(operation.getSignature());
245         if (oldOperation != null) {
246             removeDeclaredOperation(oldOperation);
247         }
248 
249         declaredOperations.add(operation);
250 
251         addOperation(operation);
252     }
253 
254     /**
255      * Called indirectly when declared operations are manipulated.
256      */
257     protected void addOperation(ClazzOperation operation) {
258         operations.add(operation);
259         operationMap.put(operation.getSignature(), operation);
260         fireOperationAdded(operation);
261     }
262 
263     public void removeDeclaredOperation(ClazzOperation operation) {
264         String signature = operation.getSignature();
265         operation = (ClazzOperation) operationMap.get(signature);
266         if (operation != null) {
267             declaredOperations.remove(operation);
268             removeOperation(operation);
269         }
270     }
271 
272     /**
273      * Called indirectly when declared operations are manipulated.
274      */
275     protected void removeOperation(ClazzOperation operation) {
276         String signature = operation.getSignature();
277         operations.remove(operation);
278         operationMap.remove(signature);
279         fireOperationRemoved(operation);
280 
281         // By deleting this declared operation, we may have exposed
282         // an inherited one
283         if (superClazz != null) {
284             operation = superClazz.getOperation(signature);
285             if (operation != null) {
286                 addOperation(operation);
287             }
288         }
289     }
290 
291     /**
292      * @see org.apache.commons.clazz.Clazz#getOperations()
293      */
294     public List getOperations() {
295         return operations;
296     }
297 
298     /**
299      * @see org.apache.commons.clazz.Clazz#getOperation(java.lang.String)
300      */
301     public ClazzOperation getOperation(String signature) {
302         return (ClazzOperation) operationMap.get(signature);
303     }
304 
305     /**
306      * @see org.apache.commons.clazz.Clazz#getInstanceFactories()
307      */
308     public List getInstanceFactories() {
309         if (instanceFactories == null) {
310             introspectInstanceFactories();
311         }
312         return Collections.unmodifiableList(instanceFactories);
313     }
314 
315     /**
316      * @see org.apache.commons.clazz.Clazz#getInstanceFactory(java.lang.String)
317      */
318     public ClazzInstanceFactory getInstanceFactory(String signature) {
319         if (instanceFactories == null) {
320             introspectInstanceFactories();
321         }
322 
323         return (ClazzInstanceFactory) instanceFactoryMap.get(signature);
324     }
325 
326     public void addInstanceFactory(ClazzInstanceFactory factory) {
327         if (factory.getDeclaringClazz() != this) {
328             throw new BeanClazzConfigurationException(
329                 "Instance factory belongs to a different clazz: "
330                     + factory.getDeclaringClazz().getName());
331         }
332 
333         if (instanceFactories == null) {
334             introspectInstanceFactories();
335         }
336         ClazzInstanceFactory oldFactory =
337             (ClazzInstanceFactory) instanceFactoryMap.get(
338                 factory.getSignature());
339         if (oldFactory != null) {
340             removeInstanceFactory(oldFactory);
341         }
342         instanceFactories.add(factory);
343         instanceFactoryMap.put(factory.getSignature(), factory);
344         fireInstanceFactoryAdded(factory);
345     }
346 
347     public void removeInstanceFactory(ClazzInstanceFactory factory) {
348         if (instanceFactories.remove(factory)) {
349             instanceFactoryMap.remove(factory.getSignature());
350             fireInstanceFactoryRemoved(factory);
351         }
352     }
353 
354     /**
355      * Finds all constructors of the <code>instanceClass</code> that have at
356      * least one argument and the first argument is of type Clazz. Wraps
357      * each of those constructors into
358      * a {@link BeanClazzConstructorInstanceFactory
359      * BeanClazzConstructorInstanceFactory} and registers it as an
360      * InstanceFactory for this clazz.
361      */
362     private void introspectInstanceFactories() {
363         instanceFactories = new ArrayList();
364         Class instanceClass = getInstanceClass();
365         Constructor constructors[] = instanceClass.getConstructors();
366         for (int i = 0; i < constructors.length; i++) {
367             Class[] parameterTypes = constructors[i].getParameterTypes();
368             if (parameterTypes != null
369                 && parameterTypes.length >= 1
370                 && Clazz.class.isAssignableFrom(parameterTypes[0])) {
371                 addInstanceFactory(
372                     new BeanClazzConstructorInstanceFactory(
373                         this,
374                         constructors[i]));
375             }
376         }
377     }
378 
379     private ClazzChangeListener listener = new ClazzChangeListener() {
380         public void propertyAdded(Clazz clazz, ClazzProperty property) {
381             if (propertyMap.get(property.getName()) == null) {
382                 addProperty(property);
383             }
384         }
385 
386         public void propertyRemoved(Clazz clazz, ClazzProperty property) {
387             ClazzProperty declared =
388                 (ClazzProperty) propertyMap.get(property.getName());
389             if (declared != null && declared.equals(property)) {
390                 removeProperty(property);
391             }
392         }
393 
394         public void operationAdded(Clazz clazz, ClazzOperation operation) {
395             if (operationMap.get(operation.getSignature()) == null) {
396                 addOperation(operation);
397             }
398         }
399 
400         public void operationRemoved(Clazz clazz, ClazzOperation operation) {
401             ClazzOperation declared =
402                 (ClazzOperation) operationMap.get(operation.getSignature());
403             if (declared != null && declared.equals(operation)) {
404                 removeOperation(operation);
405             }
406         }
407 
408         public void instanceFactoryAdded(
409             Clazz clazz,
410             ClazzInstanceFactory property) 
411         {
412             // Ignore - factories are not inherited
413         }
414 
415         public void instanceFactoryRemoved(
416             Clazz clazz,
417             ClazzInstanceFactory property) 
418         {
419             // Ignore - factories are not inherited
420         }
421     };
422 }