KickJava   Java API By Example, From Geeks To Geeks.

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


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.io.FileFilter JavaDoc;
33 import java.io.IOException JavaDoc;
34 import java.util.ArrayList JavaDoc;
35 import java.util.HashMap JavaDoc;
36
37 import org.apache.bcel.Constants;
38
39 import org.jibx.binding.def.BindingDefinition;
40 import org.jibx.runtime.JiBXException;
41
42 /**
43  * Modifiable class handler. Each instance controls changes to a particular
44  * class modified by one or more binding definitions. As methods are generated
45  * they're checked for uniqueness. If an already-generated method is found with
46  * the same characteristics (including byte code) as the one being generated,
47  * the already-generated is used instead.
48  *
49  * @author Dennis M. Sosnoski
50  * @version 1.0
51  */

52
53 public class MungedClass
54 {
55     //
56
// Constants and such related to code generation.
57

58     /** Empty class file array. */
59     private static final ClassFile[] EMPTY_CLASSFILE_ARRAY = {};
60
61     /** Name for list of binding factory names. */
62     private static final String JavaDoc BINDING_LISTNAME =
63         BindingDefinition.GENERATE_PREFIX + "bindingList";
64     
65     /** Name and signature for generated methods without standard prefix. */
66     private static final String JavaDoc[] EXTRA_METHODS_MATCHES =
67     {
68         "marshal", "(Lorg/jibx/runtime/IMarshallingContext;)V",
69         "unmarshal", "(Lorg/jibx/runtime/IUnmarshallingContext;)V"
70     };
71     
72     //
73
// Static data.
74

75     /** Munged class information. */
76     private static ArrayList JavaDoc s_classes;
77     
78     /** Map from generated class to binding information. */
79     private static HashMap JavaDoc s_classMap;
80     
81     /** Map of directories already checked for JiBX classes. */
82     private static HashMap JavaDoc s_directories;
83     
84     /** Map from class name to binding information. */
85     private static HashMap JavaDoc s_nameMap;
86     
87     /** Munged classes to be unique-added at end of binding. */
88     private static ArrayList JavaDoc s_pendingClasses;
89
90     //
91
// Actual instance data.
92

93     /** Munged class file information. */
94     private ClassFile m_classFile;
95     
96     /** Map from method byte code and signature to method item. */
97     private HashMap JavaDoc m_methodMap;
98     
99     /** Existing binding methods in class. */
100     private ExistingMethod[] m_existingMethods;
101     
102     /** List of factory names for this class. */
103     private String JavaDoc m_factoryList;
104
105     /**
106      * Constructor. This sets up for modifying a class with added methods.
107      *
108      * @param cf owning class file information
109      * @throws JiBXException on error in initializing method construction
110      */

111
112     private MungedClass(ClassFile cf) {
113         
114         // initialize basic class information
115
m_classFile = cf;
116         m_methodMap = new HashMap JavaDoc();
117         
118         // get information for existing binding methods in this class
119
ExistingMethod[] exists = cf.getBindingMethods
120             (BindingDefinition.GENERATE_PREFIX, EXTRA_METHODS_MATCHES);
121         if (exists != null) {
122             for (int i = 0; i < exists.length; i++) {
123                 m_methodMap.put(exists[i], exists[i]);
124             }
125         }
126         m_existingMethods = exists;
127     }
128
129     /**
130      * Get munged class file information.
131      *
132      * @return class file information for bound class
133      */

134
135     /*package*/ ClassFile getClassFile() {
136         return m_classFile;
137     }
138
139     /**
140      * Delete pre-existing binding methods that are no longer needed.
141      *
142      * @throws JiBXException on configuration error
143      */

144
145     private void purgeUnusedMethods() throws JiBXException {
146         if (m_existingMethods != null) {
147             for (int i = 0; i < m_existingMethods.length; i++) {
148                 ExistingMethod method = m_existingMethods[i];
149                 if (!method.isUsed()) {
150                     method.delete();
151                 }
152             }
153         }
154     }
155
156     /**
157      * Get unique method. If a method matching the byte code of the supplied
158      * method has already been defined the existing method is returned.
159      * Otherwise the method is added to the definitions. If necessary, a number
160      * suffix is appended to the method name to prevent conflicts with existing
161      * names.
162      *
163      * @param builder method to be defined
164      * @param suffix append name suffix to assure uniqueness flag
165      * @return defined method item
166      * @throws JiBXException on configuration error
167      */

168
169     /*package*/ BindingMethod getUniqueMethod(MethodBuilder builder,
170         boolean suffix) throws JiBXException {
171         
172         // try to find already added method with same characteristics
173
if (builder.getClassFile() != m_classFile) {
174             throw new IllegalStateException JavaDoc
175                 ("Internal error: wrong class for call");
176         }
177         builder.codeComplete(suffix);
178         BindingMethod method = (BindingMethod)m_methodMap.get(builder);
179         if (method == null) {
180 // System.out.println("No match found for method " +
181
// builder.getClassFile().getName()+ '.' + builder.getName() +
182
// "; adding method");
183

184             // create as new method
185
builder.addMethod();
186             m_methodMap.put(builder, builder);
187             return builder;
188             
189         } else if (method instanceof ExistingMethod) {
190             ((ExistingMethod)method).setUsed();
191         }
192 // System.out.println("Found " + method.getClassFile().getName()+
193
// '.' + method.getName() + " as match for " + builder.getName());
194
return method;
195     }
196
197     /**
198      * Get unique generated support class. Allows tracking of all generated
199      * classes for common handling with the bound classes. Each generated
200      * support class is associated with a particular bound class.
201      *
202      * @param cf generated class file
203      * @return unique class file information
204      * @throws JiBXException on configuration error
205      */

206
207     public static ClassFile getUniqueSupportClass(ClassFile cf)
208         throws JiBXException {
209         cf.codeComplete();
210         Object JavaDoc value = s_classMap.get(cf);
211         if (value == null) {
212             s_classes.add(cf);
213             s_classMap.put(cf, cf);
214 // System.out.println("Added class " + cf.getName());
215
return cf;
216         } else {
217             ClassFile prior = (ClassFile)value;
218             prior.incrementUseCount();
219 // System.out.println("Matched class " + cf.getName() + " with " +
220
// prior.getName());
221
return prior;
222         }
223     }
224
225     /**
226      * Check directory for JiBX generated files. Scans through all class files
227      * in the target directory and loads any that start with the JiBX
228      * identifier string.
229      *
230      * @param root class path root for directory
231      * @param package relative to root directory
232      * @throws JiBXException on configuration error
233      */

234
235     private static void checkDirectory(File JavaDoc root, String JavaDoc pack)
236         throws JiBXException {
237         try {
238             File JavaDoc directory = new File JavaDoc
239                 (root, pack.replace('.', File.separatorChar));
240             String JavaDoc cpath = directory.getCanonicalPath();
241             if (s_directories.get(cpath) == null) {
242                 File JavaDoc[] matches = new File JavaDoc(cpath).listFiles(new JiBXFilter());
243                 for (int i = 0; i < matches.length; i++) {
244                     File JavaDoc file = matches[i];
245                     String JavaDoc name = file.getName();
246                     int split = name.indexOf('.');
247                     if (split >= 0) {
248                         name = name.substring(0, split);
249                     }
250                     if (pack.length() > 0) {
251                         name = pack + '.' + name;
252                     }
253                     ClassFile cf = ClassCache.getClassFile(name);
254                     s_classes.add(cf);
255                     s_classMap.put(cf, cf);
256                 }
257                 s_directories.put(cpath, cpath);
258             }
259         } catch (IOException JavaDoc ex) {
260             throw new JiBXException("Error loading class file", ex);
261         }
262     }
263
264     /**
265      * Add binding factory to class. The binding factories are accumulated as
266      * a delimited string during generation, then dumped to a static field in
267      * the class at the end.
268      *
269      * @param fact binding factory name
270      */

271
272     /*package*/ void addFactory(String JavaDoc fact) {
273         if (m_factoryList == null) {
274             m_factoryList = "|" + fact + "|";
275         } else {
276             m_factoryList = m_factoryList + fact + "|";
277         }
278     }
279
280     /**
281      * Generate factory list. Adds or replaces the existing static array of
282      * factories in the class.
283      *
284      * @throws JiBXException on configuration error
285      */

286
287     /*package*/ void setFactoryList() throws JiBXException {
288         if (m_factoryList != null) {
289             short access = Constants.ACC_PUBLIC | Constants.ACC_FINAL |
290                  Constants.ACC_STATIC;
291             m_classFile.updateField("java.lang.String", BINDING_LISTNAME,
292                 access, m_factoryList);
293         }
294     }
295
296     /**
297      * Get modification tracking information for class.
298      *
299      * @param cf information for class to be modified (must be writable)
300      * @return binding information for class
301      * @throws JiBXException on configuration error
302      */

303
304     /*package*/ static MungedClass getInstance(ClassFile cf)
305         throws JiBXException {
306         MungedClass inst = (MungedClass)s_nameMap.get(cf.getName());
307         if (inst == null) {
308             inst = new MungedClass(cf);
309             s_nameMap.put(cf.getName(), inst);
310             if (cf.isComplete()) {
311                 if (s_classMap.get(cf) == null) {
312                     s_classes.add(inst);
313                     s_classMap.put(cf, cf);
314                 } else {
315                     throw new IllegalStateException JavaDoc
316                         ("Existing class conflicts with load");
317                 }
318                 String JavaDoc pack = cf.getPackage();
319                 checkDirectory(cf.getRoot(), pack);
320             }
321         }
322         inst.m_classFile.incrementUseCount();
323         return inst;
324     }
325
326     /**
327      * Add unique support class at end of binding process. This allows a class
328      * to be constructed in steps during handling of one or more bindings, with
329      * the class finished and checked for uniqueness only after all bindings
330      * have been handled. The actual add of the class is done during the
331      * {@link #writeFiles} handling.
332      *
333      * @param cf class file to be added as unique support class at end of
334      * binding
335      */

336
337     public static void delayedAddUnique(ClassFile cf) throws JiBXException {
338         s_pendingClasses.add(cf);
339     }
340
341     /**
342      * Finalize changes to modified class files.
343      *
344      * @param write replace original class files with modified versions
345      * @return three-way array of class files, for written, unmodified, and
346      * deleted
347      * @throws JiBXException on write error
348      */

349
350     public static ClassFile[][] fixChanges(boolean write) throws JiBXException {
351         try {
352             for (int i = 0; i < s_pendingClasses.size(); i++) {
353                 getUniqueSupportClass((ClassFile)s_pendingClasses.get(i));
354             }
355             ArrayList JavaDoc writes = new ArrayList JavaDoc();
356             ArrayList JavaDoc keeps = new ArrayList JavaDoc();
357             ArrayList JavaDoc deletes = new ArrayList JavaDoc();
358             for (int i = 0; i < s_classes.size(); i++) {
359                 Object JavaDoc obj = s_classes.get(i);
360                 ClassFile cf;
361                 if (obj instanceof MungedClass) {
362                     MungedClass inst = (MungedClass)obj;
363                     inst.purgeUnusedMethods();
364                     inst.setFactoryList();
365                     cf = inst.getClassFile();
366                 } else {
367                     cf = (ClassFile)obj;
368                 }
369                 if (cf.isModified()) {
370                     if (write) {
371                         cf.writeFile();
372                     }
373                     writes.add(cf);
374 // System.out.println("Wrote file " + cf.getName());
375
} else if (cf.getUseCount() > 0) {
376                     keeps.add(cf);
377 // System.out.println("Kept file " + cf.getName());
378
} else {
379                     cf.delete();
380                     deletes.add(cf);
381 // System.out.println("Deleted file " + cf.getName());
382
}
383             }
384             ClassFile[][] results = new ClassFile[3][];
385             results[0] = (ClassFile[])writes.toArray(EMPTY_CLASSFILE_ARRAY);
386             results[1] = (ClassFile[])keeps.toArray(EMPTY_CLASSFILE_ARRAY);
387             results[2] = (ClassFile[])deletes.toArray
388                 (EMPTY_CLASSFILE_ARRAY);
389             return results;
390         } catch (IOException JavaDoc ex) {
391             throw new JiBXException("Error writing to file", ex);
392         }
393     }
394
395     /**
396      * Filter for class files generated by JiBX.
397      */

398     
399     private static class JiBXFilter implements FileFilter JavaDoc
400     {
401         public boolean accept(File JavaDoc file) {
402             String JavaDoc name = file.getName();
403             return name.startsWith(BindingDefinition.GENERATE_PREFIX) &&
404                  name.endsWith(".class");
405         }
406     }
407
408     /**
409      * Discard cached information and reset in preparation for a new binding
410      * run.
411      */

412
413     public static void reset() {
414         s_classes = new ArrayList JavaDoc();
415         s_classMap = new HashMap JavaDoc();
416         s_directories = new HashMap JavaDoc();
417         s_nameMap = new HashMap JavaDoc();
418         s_pendingClasses = new ArrayList JavaDoc();
419     }
420 }
Popular Tags