KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > beehive > controls > runtime > bean > BeanPersistenceDelegate


1 package org.apache.beehive.controls.runtime.bean;
2 /*
3  * Copyright 2004 The Apache Software Foundation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * $Header:$
18  */

19
20 import java.beans.BeanInfo JavaDoc;
21 import java.beans.DefaultPersistenceDelegate JavaDoc;
22 import java.beans.Encoder JavaDoc;
23 import java.beans.EventSetDescriptor JavaDoc;
24 import java.beans.Expression JavaDoc;
25 import java.beans.IntrospectionException JavaDoc;
26 import java.beans.Introspector JavaDoc;
27 import java.beans.PersistenceDelegate JavaDoc;
28 import java.beans.PropertyDescriptor JavaDoc;
29 import java.beans.Statement JavaDoc;
30 import java.beans.XMLEncoder JavaDoc;
31 import java.lang.reflect.Field JavaDoc;
32 import java.lang.reflect.Method JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.Set JavaDoc;
35
36 import org.apache.beehive.controls.api.ControlException;
37 import org.apache.beehive.controls.api.properties.AnnotatedElementMap;
38 import org.apache.beehive.controls.api.properties.BeanPropertyMap;
39 import org.apache.beehive.controls.api.properties.PropertyKey;
40 import org.apache.beehive.controls.api.properties.PropertyMap;
41
42 /**
43  * The BeanPersistenceDelegate class supports the XML persistence of Control JavaBeans by
44  * implementing the <code>java.beans.PersistenceDelegate</b> API, and overriding the default
45  * persistence algorithm based upon the runtime structure for Controls. It selectively registers
46  * other PersistenceDelegate instances for other nested entities, as required, to ensure that
47  * runtime-defined state and object relationships are properly maintained.
48  * <p>
49  * For the BeanInfo of all generated ControlJavaBeans, a BeanPersistenceDelegate instance will
50  * be registered as the "persistenceDelegate" attribute in the BeanDescriptor. The standard
51  * <code>java.beans.Encoder</code> persistence delegate lookup mechanism recognizes this attribute
52  * and will use the instance to persist an ControlBeans written to the encoding stream.
53  * <p>
54  * The BeanPersistence class implements optimized property persistence based upon the
55  * fact that the ControlBean already has a map containing all non-default property state. Rather
56  * than using the standard (and slower) algorithm of comparing the encoding instance against a
57  * 'clean' instance, the delegate can simply retrieve the map and persist the values contained
58  * within it.
59  *
60  * @see java.beans.XMLEncoder
61  * @see java.beans.PersistenceDelegate
62  */

63 public class BeanPersistenceDelegate extends DefaultPersistenceDelegate JavaDoc
64 {
65     /**
66      * The FieldPersistencersistence is an XMLEncoder PersistenceDelegate for the
67      * <code>java.lang.reflect.Field</code> claass. It is similar to the one that comes
68      * bundled with the JDK with one key exception: it works for non-public fields as
69      * well.
70      */

71     class FieldPersistenceDelegate extends PersistenceDelegate JavaDoc
72     {
73         protected Expression JavaDoc instantiate(Object JavaDoc oldInstance, Encoder JavaDoc out)
74         {
75             Field JavaDoc f = (Field JavaDoc)oldInstance;
76             return new Expression JavaDoc(oldInstance, f.getDeclaringClass(), "getDeclaredField",
77                                     new Object JavaDoc[]{f.getName()});
78         }
79     }
80
81     /**
82      * PersistenceDelegate.instantiate()
83      */

84     protected Expression JavaDoc instantiate(Object JavaDoc oldInstance, Encoder JavaDoc out)
85     {
86         XMLEncoder JavaDoc xmlOut = (XMLEncoder JavaDoc)out;
87         ControlBean control = (ControlBean)oldInstance;
88
89         //
90
// If processing a nested control, then use the parent bean's context as the
91
// constructor context
92
//
93
ControlBeanContext cbc = null;
94         if (xmlOut.getOwner() != null)
95             cbc = ((ControlBean)xmlOut.getOwner()).getControlBeanContext();
96
97         //
98
// See if the ControlBean has any associated PropertyMap in its delegation chain
99
// that was derived from an AnnotatedElement so this relationship (and any associated
100
// external config delegates) will be restored as part of the decoding process.
101
//
102
// BUGBUG: What about a user-created PropertyMap that was passed into the constructor?
103
//
104
AnnotatedElementMap aem = null;
105         PropertyMap pMap = control.getPropertyMap();
106         while (pMap != null)
107         {
108             if (pMap instanceof AnnotatedElementMap)
109             {
110                 aem = (AnnotatedElementMap)pMap;
111
112                 //
113
// Ignore a class-valued AnnotationElementMap.. this just refers to the
114
// Control type, and will be automatically reassociated at construction
115
// time
116
//
117
if (aem.getAnnotatedElement() instanceof Class JavaDoc)
118                     aem = null;
119
120                 xmlOut.setPersistenceDelegate(AnnotatedElementMap.class,
121                                               new AnnotatedElementMapPersistenceDelegate());
122
123                 break;
124             }
125
126             pMap = pMap.getDelegateMap();
127         }
128
129
130         //
131
// Create a constructor that that uses the following form:
132
// new <BeanClass>(ControlBeanContext cbc, String id, PropertyMap map)
133
// The context is set to null, so the current active container context will be
134
// used, the id will be the ID of the original control and the map will be
135
// any AnnotatedElementMap that was passed into the original constructor.
136
//
137
return new Expression JavaDoc(control, control.getClass(), "new",
138                               new Object JavaDoc [] {cbc, control.getLocalID(), aem});
139     }
140
141     /**
142      * PersistenceDelegate.initialize()
143      */

144     protected void initialize(Class JavaDoc<?> type, Object JavaDoc oldInstance, Object JavaDoc newInstance, Encoder JavaDoc out)
145     {
146         //
147
// Get the bean and associated beanInfo for the source instance
148
//
149
ControlBean control = (ControlBean)oldInstance;
150         BeanInfo JavaDoc beanInfo;
151         try
152         {
153             beanInfo = Introspector.getBeanInfo(control.getClass());
154         }
155         catch (IntrospectionException JavaDoc ie)
156         {
157             throw new ControlException("Unable to locate BeanInfo", ie);
158         }
159
160         //
161
// Cast the encoding stream to an XMLEncoder (only encoding supported) and then set
162
// the stream owner to the bean being persisted
163
//
164
XMLEncoder JavaDoc xmlOut = (XMLEncoder JavaDoc)out;
165         Object JavaDoc owner = xmlOut.getOwner();
166         xmlOut.setOwner(control);
167         try
168         {
169
170             //
171
// The default implementation of property persistence will use BeanInfo to
172
// incrementally compare oldInstance property values to newInstance property values.
173
// Because the bean instance PropertyMap holds only the values that have been
174
// modified, this process can be optimized by directly writing out only the properties
175
// found in the map.
176
//
177
BeanPropertyMap beanMap = control.getPropertyMap();
178             PropertyDescriptor JavaDoc [] propDescriptors = beanInfo.getPropertyDescriptors();
179             for (PropertyKey pk : beanMap.getPropertyKeys())
180             {
181                 //
182
// Locate the PropertyDescriptor for the modified property, and use it to write
183
// the property value to the encoder stream
184
//
185
String JavaDoc propName = pk.getPropertyName();
186                 boolean found = false;
187                 for (int i = 0; i < propDescriptors.length; i++)
188                 {
189                     if (propName.equals(propDescriptors[i].getName()))
190                     {
191                         found = true;
192
193                         // Only write the property if it is not flagged as transient
194
Object JavaDoc transientVal = propDescriptors[i].getValue("transient");
195                         if (transientVal == null || transientVal.equals(Boolean.FALSE))
196                         {
197                             xmlOut.writeStatement(
198                                 new Statement JavaDoc(oldInstance,
199                                       propDescriptors[i].getWriteMethod().getName(),
200                                       new Object JavaDoc [] {beanMap.getProperty(pk)}));
201                         }
202                     }
203                 }
204                 if (found == false)
205                 {
206                     throw new ControlException("Unknown property in bean PropertyMap: " + pk);
207                 }
208             }
209
210             //
211
// Get the bean context associated with the bean, and persist any nested controls
212
//
213
ControlBeanContext cbc = control.getControlBeanContext();
214             if (cbc.size() != 0)
215             {
216                 xmlOut.setPersistenceDelegate(ControlBeanContext.class,
217                                               new ContextPersistenceDelegate());
218
219                 Iterator JavaDoc nestedIter = cbc.iterator();
220                 while (nestedIter.hasNext())
221                 {
222                     Object JavaDoc bean = nestedIter.next();
223                     if (bean instanceof ControlBean)
224                     {
225                         xmlOut.writeStatement(
226                             new Statement JavaDoc(cbc, "add", new Object JavaDoc [] { bean } ));
227                     }
228                 }
229             }
230
231             //
232
// Restore any listeners associated with the control
233
//
234
EventSetDescriptor JavaDoc [] eventSetDescriptors = beanInfo.getEventSetDescriptors();
235             for (int i = 0; i < eventSetDescriptors.length; i++)
236             {
237                 EventSetDescriptor JavaDoc esd = eventSetDescriptors[i];
238                 Method JavaDoc listenersMethod = esd.getGetListenerMethod();
239                 String JavaDoc addListenerName = esd.getAddListenerMethod().getName();
240                 if (listenersMethod != null)
241                 {
242                     //
243
// Get the list of listeners, and then add statements to incrementally
244
// add them in the same order
245
//
246
try
247                     {
248                         Object JavaDoc [] lstnrs = (Object JavaDoc [])listenersMethod.invoke(control,
249                                                                              new Object JavaDoc []{});
250                         for (int j = 0; j < lstnrs.length; j++)
251                         {
252                             //
253
// If this is a generated EventAdaptor class, then set the delegate
254
// explicitly
255
//
256
if (lstnrs[j] instanceof EventAdaptor)
257                                 xmlOut.setPersistenceDelegate(lstnrs[j].getClass(),
258                                                               new AdaptorPersistenceDelegate());
259                             xmlOut.writeStatement(
260                                 new Statement JavaDoc(control, addListenerName, new Object JavaDoc [] {lstnrs[j]}));
261                         }
262                     }
263                     catch (Exception JavaDoc iae)
264                     {
265                         throw new ControlException("Unable to initialize listeners", iae);
266                     }
267                 }
268             }
269
270             //
271
// See if the control holds an implementation instance, if so, we need to include
272
// it (and any nested controls or state) in the encoding stream
273
//
274
Object JavaDoc impl = control.getImplementation();
275             if (impl != null)
276             {
277
278                 //
279
// Set the persistence delegate for the impl class to the Impl delegate,
280
// set the current stream owner to the bean, and then write the implementation
281
//
282
Class JavaDoc implClass = impl.getClass();
283                 if (xmlOut.getPersistenceDelegate(implClass) instanceof DefaultPersistenceDelegate JavaDoc)
284                     xmlOut.setPersistenceDelegate(implClass, new ImplPersistenceDelegate());
285
286                 //
287
// HACK: This bit of hackery pushes the impl into the persistence stream
288
// w/out actually requiring it be used as an argument elsewhere, since there
289
// is no public API on the bean that takes an impl instance as an argument.
290
//
291
xmlOut.writeStatement(
292                     new Statement JavaDoc(impl, "toString", null));
293             }
294         }
295         finally
296         {
297             // Restore the previous encoding stream owner
298
xmlOut.setOwner(owner);
299         }
300     }
301
302     /**
303      * PersistenceDelegate.writeObject()
304      */

305     public void writeObject(Object JavaDoc oldInstance, Encoder JavaDoc out)
306     {
307         // Override the default FieldPersistence algorithm for the encoder, so private fields
308
// can also be encoded
309
out.setPersistenceDelegate(Field JavaDoc.class, new FieldPersistenceDelegate());
310         super.writeObject(oldInstance, out);
311     }
312 }
313
Popular Tags