KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jibx > binding > classes > BoundClass


1 /*
2 Copyright (c) 2003-2005, Dennis M. Sosnoski
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7
8  * Redistributions of source code must retain the above copyright notice, this
9    list of conditions and the following disclaimer.
10  * Redistributions in binary form must reproduce the above copyright notice,
11    this list of conditions and the following disclaimer in the documentation
12    and/or other materials provided with the distribution.
13  * Neither the name of JiBX nor the names of its contributors may be used
14    to endorse or promote products derived from this software without specific
15    prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */

28
29 package org.jibx.binding.classes;
30
31 import java.io.File JavaDoc;
32 import java.util.HashMap JavaDoc;
33
34 import org.apache.bcel.Constants;
35 import org.apache.bcel.classfile.Utility;
36 import org.apache.bcel.generic.Type;
37
38 import org.jibx.binding.def.BindingDefinition;
39 import org.jibx.runtime.JiBXException;
40
41 /**
42  * Bound class handler. Each instance controls and organizes information for a
43  * class included in one or more binding definitions.
44  *
45  * @author Dennis M. Sosnoski
46  * @version 1.0
47  */

48
49 public class BoundClass
50 {
51     //
52
// Constants and such related to code generation.
53

54     /** Class used for code munging when no specific class available. */
55     private static final String JavaDoc GENERIC_MUNGE_CLASS =
56         BindingDefinition.GENERATE_PREFIX + "MungeAdapter";
57     
58     /** Prefix used for access methods. */
59     private static final String JavaDoc ACCESS_PREFIX =
60         BindingDefinition.GENERATE_PREFIX + "access_";
61     
62     /** Empty argument type array. */
63     private static final Type[] EMPTY_TYPE_ARGS = {};
64     
65     //
66
// Static data.
67

68     /** Map from bound class name (or bound and munged combination) to binding
69      information. */

70     private static HashMap JavaDoc s_nameMap;
71     
72     /** Package of first modifiable class. */
73     private static String JavaDoc s_modifyPackage;
74     
75     /** Root for package of first modifiable class. */
76     private static File JavaDoc s_modifyRoot;
77     
78     /** Class used for code generation proxy with unmodifiable classes. */
79     private static MungedClass s_genericMunge;
80
81     //
82
// Actual instance data.
83

84     /** Bound class file information. */
85     private final ClassFile m_boundClass;
86     
87     /** Class receiving code generated for target class. */
88     private final MungedClass m_mungedClass;
89     
90     /** Map from field or method to load access method (lazy create,
91      <code>null</code> if not used). */

92     private HashMap JavaDoc m_loadMap;
93     
94     /** Map from field or method to store access method (lazy create,
95      <code>null</code> if not used). */

96     private HashMap JavaDoc m_storeMap;
97
98     /**
99      * Constructor.
100      *
101      * @param bound target class file information
102      * @param munge class file for class hosting generated code
103      */

104
105     private BoundClass(ClassFile bound, MungedClass munge) {
106         m_boundClass = bound;
107         m_mungedClass = munge;
108     }
109
110     /**
111      * Get bound class file information.
112      *
113      * @return class file information for bound class
114      */

115
116     public ClassFile getClassFile() {
117         return m_boundClass;
118     }
119
120     /**
121      * Get bound class file name.
122      *
123      * @return name of bound class
124      */

125
126     public String JavaDoc getClassName() {
127         return m_boundClass.getName();
128     }
129
130     /**
131      * Get munged class file information.
132      *
133      * @return class file information for class being modified
134      */

135
136     public ClassFile getMungedFile() {
137         return m_mungedClass.getClassFile();
138     }
139
140     /**
141      * Check if class being changed directly.
142      *
143      * @return <code>true</code> if bound class is being modified,
144      * <code>false</code> if using a surrogate
145      */

146
147     public boolean isDirectAccess() {
148         return m_boundClass == m_mungedClass.getClassFile();
149     }
150
151     /**
152      * Get load access method for member of this class. If the access method
153      * does not already exist it's created by this call. If the access method
154      * does exist but without access from the context class, the access
155      * permission on the method is broadened (from package to protected or
156      * public, or from protected to public).
157      *
158      * @param item field or method to be accessed
159      * @param from context class from which access is required
160      * @return the item itself if it's accessible from the required context,
161      * an access method that is accessible if the item is not itself
162      * @throws JiBXException on configuration error
163      */

164
165     public ClassItem getLoadMethod(ClassItem item, ClassFile from)
166         throws JiBXException {
167         
168         // initialize tracking information for access methods if first time
169
if (m_loadMap == null) {
170             m_loadMap = new HashMap JavaDoc();
171         }
172         
173         // check if a new access method needed
174
BindingMethod method = (BindingMethod)m_loadMap.get(item);
175         if (method == null) {
176             
177             // set up for constructing new method
178
String JavaDoc name = ACCESS_PREFIX + "load_" + item.getName();
179             ClassFile cf = item.getClassFile();
180             Type type = Type.getType(Utility.getSignature(item.getTypeName()));
181             MethodBuilder mb = new ExceptionMethodBuilder(name, type,
182                 EMPTY_TYPE_ARGS, cf, (short)0);
183             
184             // add the actual access method code
185
mb.appendLoadLocal(0);
186             if (item.isMethod()) {
187                 mb.addMethodExceptions(item);
188                 mb.appendCall(item);
189             } else {
190                 mb.appendGetField(item);
191             }
192             mb.appendReturn(type);
193             
194             // track unique instance of this method
195
method = m_mungedClass.getUniqueMethod(mb, true);
196             m_loadMap.put(item, method);
197         }
198         
199         // make sure method is accessible
200
method.makeAccessible(from);
201         return method.getItem();
202     }
203
204     /**
205      * Get store access method for member of this class. If the access method
206      * does not already exist it's created by this call. If the access method
207      * does exist but without access from the context class, the access
208      * permission on the method is broadened (from package to protected or
209      * public, or from protected to public).
210      *
211      * @param item field or method to be accessed
212      * @param from context class from which access is required
213      * @return the item itself if it's accessible from the required context,
214      * an access method that is accessible if the item is not itself
215      * @throws JiBXException on configuration error
216      */

217
218     public ClassItem getStoreMethod(ClassItem item, ClassFile from)
219         throws JiBXException {
220         
221         // initialize tracking information for access methods if first time
222
if (m_storeMap == null) {
223             m_storeMap = new HashMap JavaDoc();
224         }
225         
226         // check if a new access method needed
227
BindingMethod method = (BindingMethod)m_storeMap.get(item);
228         if (method == null) {
229             
230             // set up for constructing new method
231
String JavaDoc name = ACCESS_PREFIX + "store_" + item.getName();
232             ClassFile cf = item.getClassFile();
233             Type type;
234             if (item.isMethod()) {
235                 String JavaDoc sig = item.getSignature();
236                 int start = sig.indexOf('(');
237                 int end = sig.indexOf(')');
238                 type = Type.getType(sig.substring(start+1, end));
239             } else {
240                 type = Type.getType(Utility.getSignature(item.getTypeName()));
241             }
242             MethodBuilder mb = new ExceptionMethodBuilder(name, Type.VOID,
243                 new Type[] {type}, cf, (short)0);
244             
245             // add the actual access method code
246
mb.appendLoadLocal(0);
247             mb.appendLoadLocal(1);
248             if (item.isMethod()) {
249                 mb.addMethodExceptions(item);
250                 mb.appendCall(item);
251             } else {
252                 mb.appendPutField(item);
253             }
254             mb.appendReturn();
255             
256             // track unique instance of this method
257
method = m_mungedClass.getUniqueMethod(mb, true);
258             m_storeMap.put(item, method);
259         }
260         
261         // make sure method is accessible
262
method.makeAccessible(from);
263         return method.getItem();
264     }
265
266     /**
267      * Get unique method. Just delegates to the modified class handling, with
268      * unique suffix appended to method name.
269      *
270      * @param builder method to be defined
271      * @return defined method item
272      * @throws JiBXException on configuration error
273      */

274
275     public BindingMethod getUniqueMethod(MethodBuilder builder)
276         throws JiBXException {
277         return m_mungedClass.getUniqueMethod(builder, true);
278     }
279
280     /**
281      * Get unique method. Just delegates to the modified class handling. The
282      * supplied name is used without change.
283      *
284      * @param builder method to be defined
285      * @return defined method item
286      * @throws JiBXException on configuration error
287      */

288
289     public BindingMethod getUniqueNamed(MethodBuilder builder)
290         throws JiBXException {
291         return m_mungedClass.getUniqueMethod(builder, false);
292     }
293
294     /**
295      * Add binding factory to class. Makes sure that there's no surrogate class
296      * for code generation, then delegates to the modified class handling.
297      *
298      * @param fact binding factory name
299      */

300
301     public void addFactory(String JavaDoc fact) {
302         if (isDirectAccess()) {
303             m_mungedClass.addFactory(fact);
304         } else {
305             throw new IllegalStateException JavaDoc
306                 ("Internal error: not directly modifiable class");
307         }
308     }
309
310     /**
311      * Generate factory list. Makes sure that there's no surrogate class for
312      * code generation, then delegates to the modified class handling.
313      *
314      * @throws JiBXException on configuration error
315      */

316
317     public void setFactoryList() throws JiBXException {
318         if (isDirectAccess()) {
319             m_mungedClass.setFactoryList();
320         } else {
321             throw new IllegalStateException JavaDoc
322                 ("Internal error: not directly modifiable class");
323         }
324     }
325
326     /**
327      * Create binding information for class. This creates the combination of
328      * bound class and (if different) munged class and adds it to the internal
329      * tables.
330      *
331      * @param key text identifier for this bound class and munged class
332      * combination
333      * @param bound class information for bound class
334      * @param munge information for surrogate class receiving generated code, or
335      * <code>null</code> if no separate class
336      * @return binding information for class
337      */

338
339     private static BoundClass createInstance(String JavaDoc key, ClassFile bound,
340         MungedClass munge) {
341         BoundClass inst = new BoundClass(bound, munge);
342         s_nameMap.put(key, inst);
343         return inst;
344     }
345
346     /**
347      * Find or create binding information for class. If the combination of bound
348      * class and munged class already exists it's returned directly, otherwise
349      * it's created and returned.
350      *
351      * @param bound class information for bound class
352      * @param munge information for surrogate class receiving generated code
353      * @return binding information for class
354      */

355
356     private static BoundClass findOrCreateInstance(ClassFile bound,
357         MungedClass munge) {
358         String JavaDoc key = bound.getName()+ ':' + munge.getClassFile().getName();
359         BoundClass inst = (BoundClass)s_nameMap.get(key);
360         if (inst == null) {
361             inst = createInstance(key, bound, munge);
362         }
363         return inst;
364     }
365
366     /**
367      * Get binding information for class. This finds the class in which code
368      * generation for the target class takes place. Normally this class will be
369      * the target class itself, but in cases where the target class is not
370      * modifiable an alternate class will be used. This can take two forms. If
371      * the context class is provided and it is a subclass of the target class,
372      * code for the target class is instead added to the context class. If
373      * there is no context class, or if the context class is not a subclass of
374      * the target class, a unique catch-all class is used.
375      *
376      * @param cf bound class information
377      * @param context context class for code generation, or <code>null</code> if
378      * no context
379      * @return binding information for class
380      * @throws JiBXException
381      * @throws JiBXException on configuration error
382      */

383
384     public static BoundClass getInstance(ClassFile cf, BoundClass context)
385         throws JiBXException {
386         
387         // check if new instance needed for this class
388
BoundClass inst = (BoundClass)s_nameMap.get(cf.getName());
389         if (inst == null) {
390             
391             // load the basic class information and check for modifiable
392
if (!cf.isInterface() && cf.isModifiable()) {
393                 
394                 // return instance directly
395
inst = createInstance(cf.getName(), cf,
396                     MungedClass.getInstance(cf));
397                 
398             } else {
399                 
400                 // see if the context class is a subclass
401
if (context != null &&
402                     context.getClassFile().isSuperclass(cf.getName())) {
403                     
404                     // find or create munge with subclass as surrogate
405
inst = findOrCreateInstance(cf, context.m_mungedClass);
406                     
407                 } else {
408                     
409                     // use catch-all munge class as surrogate for all else
410
if (s_genericMunge == null) {
411                         String JavaDoc mname = (s_modifyPackage == null) ?
412                             GENERIC_MUNGE_CLASS :
413                             s_modifyPackage + '.' + GENERIC_MUNGE_CLASS;
414                         ClassFile base = ClassCache.
415                             getClassFile("java.lang.Object");
416                         int acc = Constants.ACC_PUBLIC|Constants.ACC_ABSTRACT;
417                         ClassFile gen = new ClassFile(mname,
418                             s_modifyRoot, base, acc, new String JavaDoc[0]);
419                         gen.addDefaultConstructor();
420                         s_genericMunge = MungedClass.getInstance(gen);
421                         MungedClass.delayedAddUnique(gen);
422                     }
423                     inst = findOrCreateInstance(cf, s_genericMunge);
424                     
425                 }
426             }
427         }
428         return inst;
429     }
430
431     /**
432      * Get binding information for class. This version takes a fully-qualified
433      * class name, calling the paired method if necessary to create a new
434      * instance.
435      *
436      * @param name fully qualified name of bound class
437      * @param context context class for code generation, or <code>null</code> if
438      * no context
439      * @return binding information for class
440      * @throws JiBXException on configuration error
441      * TODO: Eliminate in 2.0 builds
442      */

443
444     public static BoundClass getInstance(String JavaDoc name, BoundClass context)
445         throws JiBXException {
446         
447         // check if new instance needed for this class
448
BoundClass inst = (BoundClass)s_nameMap.get(name);
449         if (inst == null) {
450             ClassFile cf = ClassCache.getClassFile(name);
451             return getInstance(cf, context);
452         }
453         return inst;
454     }
455
456     /**
457      * Discard cached information and reset in preparation for a new binding
458      * run.
459      */

460
461     public static void reset() {
462         s_nameMap = new HashMap JavaDoc();
463         s_modifyPackage = null;
464         s_modifyRoot = null;
465         s_genericMunge = null;
466     }
467
468     /**
469      * Discard cached information and reset in preparation for a new binding
470      * run.
471      */

472
473     public static void setModify(File JavaDoc root, String JavaDoc pkg) {
474         s_modifyRoot = root;
475         s_modifyPackage = pkg;
476         if (s_modifyPackage.length() == 0) {
477             s_modifyPackage = null;
478         }
479     }
480 }
Popular Tags