KickJava   Java API By Example, From Geeks To Geeks.

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


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.FileInputStream JavaDoc;
33 import java.io.FileOutputStream JavaDoc;
34 import java.io.IOException JavaDoc;
35 import java.io.InputStream JavaDoc;
36 import java.io.OutputStream JavaDoc;
37 import java.net.MalformedURLException JavaDoc;
38 import java.net.URL JavaDoc;
39 import java.net.URLClassLoader JavaDoc;
40 import java.util.ArrayList JavaDoc;
41 import java.util.Arrays JavaDoc;
42 import java.util.HashMap JavaDoc;
43
44 import org.apache.bcel.Constants;
45 import org.apache.bcel.classfile.ClassParser;
46 import org.apache.bcel.classfile.Code;
47 import org.apache.bcel.classfile.CodeException;
48 import org.apache.bcel.classfile.Constant;
49 import org.apache.bcel.classfile.ConstantDouble;
50 import org.apache.bcel.classfile.ConstantFloat;
51 import org.apache.bcel.classfile.ConstantInteger;
52 import org.apache.bcel.classfile.ConstantLong;
53 import org.apache.bcel.classfile.ConstantPool;
54 import org.apache.bcel.classfile.ConstantString;
55 import org.apache.bcel.classfile.ConstantUtf8;
56 import org.apache.bcel.classfile.ConstantValue;
57 import org.apache.bcel.classfile.ExceptionTable;
58 import org.apache.bcel.classfile.Field;
59 import org.apache.bcel.classfile.FieldOrMethod;
60 import org.apache.bcel.classfile.JavaClass;
61 import org.apache.bcel.classfile.Method;
62 import org.apache.bcel.classfile.Utility;
63 import org.apache.bcel.generic.ClassGen;
64 import org.apache.bcel.generic.ConstantPoolGen;
65 import org.apache.bcel.generic.FieldGen;
66 import org.apache.bcel.generic.Type;
67 import org.apache.bcel.util.ClassPath;
68 import org.jibx.runtime.JiBXException;
69
70 /**
71  * Class file information. Wraps the actual class file data as well as
72  * associated management information.
73  *
74  * @author Dennis M. Sosnoski
75  * @version 1.0
76  */

77
78 public class ClassFile
79 {
80     //
81
// Constants for code generation.
82

83     public static final int PRIVATE_ACCESS = 0;
84     public static final int PACKAGE_ACCESS = 1;
85     public static final int PROTECTED_ACCESS = 2;
86     public static final int PUBLIC_ACCESS = 3;
87     
88     protected static final int PRIVATEFIELD_ACCESS = Constants.ACC_PRIVATE;
89     protected static final ExistingMethod[] EMPTY_METHOD_ARRAY = {};
90     
91     //
92
// Class data.
93

94     /** Singleton loader from classpath. */
95     private static ClassPath s_loader;
96     
97     /** Direct class loader. */
98     private static ClassLoader JavaDoc s_directLoader;
99         
100     //
101
// Actual instance data.
102

103     /** Fully qualified class name. */
104     private String JavaDoc m_name;
105
106     /** Signature for class as type. */
107     private String JavaDoc m_signature;
108     
109     /** Class as type. */
110     private Type m_type;
111
112     /** Directory root for class. */
113     private File JavaDoc m_root;
114
115     /** Actual class file information. */
116     private File JavaDoc m_file;
117
118     /** Class in same package as superclass flag. */
119     private boolean m_isSamePackage;
120
121     /** File is writable flag. */
122     private boolean m_isWritable;
123
124     /** Super class of this class (set by caller, since it may require
125      additional information to find the class file). */

126     protected ClassFile m_superClass;
127     
128     /** Names of all interfaces directly implemented by this class. */
129     protected String JavaDoc[] m_interfaces;
130     
131     /** All classes and interfaces of which this is an instance (lazy create,
132      only if needed. */

133     private String JavaDoc[] m_instanceOfs;
134
135     /** Base class information as loaded by BCEL. */
136     private JavaClass m_curClass;
137
138     /** Modified class generator (lazy create, only if needed). */
139     private ClassGen m_genClass;
140
141     /** Constant pool generator for modified class (lazy create, only if
142      needed). */

143     private ConstantPoolGen m_genPool;
144
145     /** Instruction factory for modified class (lazy create, only if needed). */
146     protected InstructionBuilder m_instBuilder;
147     
148     /** Map for method names with possibly generated suffixes (lazy create, only
149      if needed). */

150     private HashMap JavaDoc m_suffixMap;
151     
152     /** Map to class item information. */
153     private HashMap JavaDoc m_itemMap;
154     
155     /** Flag for class modified. */
156     private boolean m_isModified;
157     
158     /** Usage count for this class. */
159     private int m_useCount;
160     
161     /** Hash code computation for class is current flag. */
162     private boolean m_isHashCurrent;
163     
164     /** Cached hash code value for class. */
165     private int m_hashCode;
166     
167     /** Depth of superclass hierarchy for class (lazy computation). */
168     private int m_inheritDepth;
169     
170     /** Suffix number for making method names unique (lazy computation). */
171     private int m_uniqueIndex;
172
173     /**
174      * Constructor for class file loaded from a stream. Loads the class data
175      * and prepares it for use.
176      *
177      * @param name fully qualified class name
178      * @param path class file path
179      * @param ins input stream for class file data
180      * @throws JiBXException if unable to load class file
181      */

182
183     public ClassFile(String JavaDoc name, String JavaDoc path, InputStream JavaDoc ins)
184         throws JiBXException {
185         init(name, path, ins);
186     }
187
188     /**
189      * Constructor for preexisting class file. Loads the class data and
190      * prepares it for use.
191      *
192      * @param name fully qualified class name
193      * @param root directory root from class loading path list
194      * @param file actual class file
195      * @throws IOException if unable to open file
196      * @throws JiBXException if error in reading class file
197      */

198
199     public ClassFile(String JavaDoc name, File JavaDoc root, File JavaDoc file)
200         throws IOException JavaDoc, JiBXException {
201         init(name, root.getPath(), new FileInputStream JavaDoc(file));
202         m_root = root;
203         m_file = file;
204         m_isWritable = file.canWrite();
205     }
206
207     /**
208      * Constructor for preexisting class file from classpath. Loads the class
209      * data and prepares it for use.
210      *
211      * @param name fully qualified class name
212      * @throws IOException if unable to open file
213      * @throws JiBXException if error in reading class file
214      */

215
216     public ClassFile(String JavaDoc name) throws IOException JavaDoc, JiBXException {
217         
218         // try out class path first, then BCEL system path
219
ClassPath.ClassFile cf = null;
220         try {
221              cf = s_loader.getClassFile(name);
222         } catch (IOException JavaDoc ex) {
223             try {
224                 cf = ClassPath.SYSTEM_CLASS_PATH.getClassFile(name);
225             } catch (IOException JavaDoc ex1) { /* deliberately left empty */ }
226         }
227         if (cf == null) {
228             throw new JiBXException("Class " + name +
229                 " not found in any classpath");
230         } else {
231             init(name, cf.getPath(), cf.getInputStream());
232         }
233     }
234
235     /**
236      * Constructor for synthetic placeholder classfile with no backing class
237      * data.
238      *
239      * @param name fully qualified class name
240      * @param sig corresponding class signature
241      */

242
243     public ClassFile(String JavaDoc name, String JavaDoc sig) {
244         m_name = name;
245         m_signature = sig;
246         m_type = Type.getType(sig);
247         m_interfaces = new String JavaDoc[0];
248         m_itemMap = new HashMap JavaDoc();
249     }
250
251     /**
252      * Constructor for new class file. Initializes the class data and
253      * prepares it for use.
254      *
255      * @param name fully qualified class name
256      * @param root directory root from class loading path list
257      * @param sclas superclass of new class
258      * @param access access flags for class
259      * @param impls array of interfaces implemented by new class
260      */

261
262     public ClassFile(String JavaDoc name, File JavaDoc root, ClassFile sclas, int access,
263         String JavaDoc[] impls) throws JiBXException {
264         String JavaDoc fname = name.replace('.', File.separatorChar)+".class";
265         File JavaDoc file = new File JavaDoc(root, fname);
266         m_name = name;
267         m_signature = Utility.getSignature(name);
268         m_type = ClassItem.typeFromName(name);
269         m_root = root;
270         m_superClass = sclas;
271         m_interfaces = impls;
272         m_file = file;
273         m_isWritable = true;
274         m_genClass = new ClassGen(name, sclas.getName(), "", access, impls);
275         m_genPool = m_genClass.getConstantPool();
276         m_instBuilder = new InstructionBuilder(m_genClass, m_genPool);
277         m_itemMap = new HashMap JavaDoc();
278         ClassCache.addClassFile(this);
279     }
280
281     /**
282      * Internal initialization method. This is used to handle common
283      * initialization for the constructors.
284      *
285      * @param name fully qualified class name
286      * @param path class file path
287      * @param ins input stream for class file data
288      * @throws JiBXException if unable to load class file
289      */

290
291     private void init(String JavaDoc name, String JavaDoc path, InputStream JavaDoc ins)
292         throws JiBXException {
293         m_name = name;
294         m_signature = Utility.getSignature(name);
295         m_type = ClassItem.typeFromName(name);
296         m_itemMap = new HashMap JavaDoc();
297         if (path == null) {
298             m_interfaces = new String JavaDoc[0];
299         } else {
300             String JavaDoc fname = name.replace('.', File.separatorChar) + ".class";
301             ClassParser parser = new ClassParser(ins, fname);
302             try {
303                 m_curClass = parser.parse();
304                 m_interfaces = m_curClass.getInterfaceNames();
305             } catch (Exception JavaDoc ex) {
306                 throw new JiBXException("Error reading path " +
307                     path + " for class " + name);
308             }
309         }
310     }
311
312     /**
313      * Check if class is an interface. This only checks existing classes,
314      * assuming that no generated classes are interfaces.
315      *
316      * @return <code>true</code> if an interface, <code>false</code> if not
317      */

318
319     public boolean isInterface() {
320         return m_curClass != null && m_curClass.isInterface();
321     }
322
323     /**
324      * Check if class is abstract. This only checks existing classes,
325      * assuming that no generated classes are abstract.
326      *
327      * @return <code>true</code> if an abstract class, <code>false</code> if not
328      */

329
330     public boolean isAbstract() {
331         return m_curClass != null && m_curClass.isAbstract();
332     }
333
334     /**
335      * Check if class is modifiable.
336      *
337      * @return <code>true</code> if class is modifiable, <code>false</code> if
338      * not
339      */

340
341     public boolean isModifiable() {
342         return m_isWritable && !isInterface();
343     }
344
345     /**
346      * Get fully qualified class name.
347      *
348      * @return fully qualified name for class
349      */

350
351     public String JavaDoc getName() {
352         return m_name;
353     }
354
355     /**
356      * Get signature for class as type.
357      *
358      * @return signature for class used as type
359      */

360
361     public String JavaDoc getSignature() {
362         return m_signature;
363     }
364
365     /**
366      * Get class as type.
367      *
368      * @return class as type
369      */

370
371     public Type getType() {
372         return m_type;
373     }
374
375     /**
376      * Get package name.
377      *
378      * @return package name for class
379      */

380
381     public String JavaDoc getPackage() {
382         int split = m_name.lastIndexOf('.');
383         if (split >= 0) {
384             return m_name.substring(0, split);
385         } else {
386             return "";
387         }
388     }
389
390     /**
391      * Get root directory for load path.
392      *
393      * @return root directory in path used for loading file
394      */

395
396     public File JavaDoc getRoot() {
397         return m_root;
398     }
399
400     /**
401      * Get actual file for class.
402      *
403      * @return file used for class
404      */

405
406     public File JavaDoc getFile() {
407         return m_file;
408     }
409
410     /**
411      * Get raw current class information.
412      *
413      * @return raw current class information
414      */

415
416     public JavaClass getRawClass() {
417         if (m_curClass == null) {
418             throw new IllegalStateException JavaDoc
419                 ("No loadable class information for " + m_name);
420         } else {
421             return m_curClass;
422         }
423     }
424
425     /**
426      * Get superclass name.
427      *
428      * @return fully qualified name of superclass
429      */

430
431     public String JavaDoc getSuperName() {
432         if (m_curClass == null) {
433             return null;
434         } else {
435             return m_curClass.getSuperclassName();
436         }
437     }
438
439     /**
440      * Set superclass information.
441      *
442      * @param sclas superclass information
443      */

444
445     public void setSuperFile(ClassFile sclas) {
446         m_superClass = sclas;
447         m_isSamePackage = getPackage().equals(sclas.getPackage());
448     }
449
450     /**
451      * Get superclass information.
452      *
453      * @return super class information as loaded (<code>null</code> if no
454      * superclass - java.lang.Object, interface, or primitive)
455      */

456
457     public ClassFile getSuperFile() {
458         return m_superClass;
459     }
460
461     /**
462      * Get names of all interfaces implemented by class.
463      *
464      * @return names of all interfaces implemented directly by class
465      */

466
467     public String JavaDoc[] getInterfaces() {
468         return m_interfaces;
469     }
470
471     /**
472      * Add interface to class. The interface is added to the class if not
473      * already defined.
474      *
475      * @param intf fully qualified interface name
476      * @return <code>true</code> if added, <code>false</code> if already present
477      * @throws JiBXException on configuration error
478      */

479
480     public boolean addInterface(String JavaDoc intf) throws JiBXException {
481         ClassGen gen = getClassGen();
482         String JavaDoc[] intfs = gen.getInterfaceNames();
483         for (int i = 0; i < intfs.length; i++) {
484             if (intf.equals(intfs[i])) {
485                 return false;
486             }
487         }
488         gen.addInterface(intf);
489         m_isModified = true;
490         m_instanceOfs = null;
491         return true;
492     }
493
494     /**
495      * Accumulate interface signatures recursively.
496      *
497      * @param intfs names of interfaces implemented
498      * @param map map for interfaces already accumulated
499      * @param accs accumulated interface names
500      * @throws JiBXException if configuration error
501      */

502
503     protected void accumulateInterfaces(String JavaDoc[] intfs, HashMap JavaDoc map,
504         ArrayList JavaDoc accs) throws JiBXException {
505         for (int i = 0; i < intfs.length; i++) {
506             String JavaDoc name = intfs[i];
507             if (map.get(name) == null) {
508                 ClassFile cf = ClassCache.getClassFile(name);
509                 String JavaDoc sig = cf.getSignature();
510                 map.put(name, sig);
511                 accs.add(sig);
512                 String JavaDoc[] inherits = cf.m_curClass.getInterfaceNames();
513                 accumulateInterfaces(inherits, map, accs);
514             }
515         }
516     }
517
518     /**
519      * Get signatures for all types of which instances of this type are
520      * instances.
521      *
522      * @return all signatures suppored by instances
523      * @throws JiBXException if configuration error
524      */

525
526     public String JavaDoc[] getInstanceSigs() throws JiBXException {
527         if (m_instanceOfs == null) {
528             
529             // check for an array class
530
String JavaDoc name = getName();
531             if (name.endsWith("[]")) {
532                 
533                 // accumulate prefix by stripping suffixes
534
String JavaDoc prefix = "";
535                 do {
536                     name = name.substring(0, name.length()-2);
537                     prefix = prefix + '[';
538                 } while (name.endsWith("[]"));
539                 
540                 // check for a primitive base type on array
541
String JavaDoc[] bsigs;
542                 if (ClassItem.isPrimitive(name)) {
543                     bsigs = new String JavaDoc[1];
544                     bsigs[0] = ClassItem.getPrimitiveSignature(name);
545                 } else {
546                     ClassFile bcf = ClassCache.getClassFile(name);
547                     bsigs = bcf.getInstanceSigs();
548                 }
549                 
550                 // derive array signatures from signatures for base type
551
String JavaDoc[] asigs = new String JavaDoc[bsigs.length];
552                 for (int i = 0; i < bsigs.length; i++) {
553                     asigs[i] = prefix + bsigs[i];
554                 }
555                 m_instanceOfs = asigs;
556                 
557             } else {
558                 
559                 // walk all classes and interfaces to find signatures
560
HashMap JavaDoc map = new HashMap JavaDoc();
561                 ArrayList JavaDoc iofs = new ArrayList JavaDoc();
562                 ClassFile cur = this;
563                 while (cur != null) {
564                     String JavaDoc sig = cur.getSignature();
565                     map.put(name, sig);
566                     iofs.add(sig);
567                     accumulateInterfaces(cur.getInterfaces(), map, iofs);
568                     cur = cur.getSuperFile();
569                 }
570                 String JavaDoc[] sigs = new String JavaDoc[iofs.size()];
571                 m_instanceOfs = (String JavaDoc[])iofs.toArray(sigs);
572                 
573             }
574         }
575         return m_instanceOfs;
576     }
577
578     /**
579      * Check if class implements an interface.
580      *
581      * @param signature of interface to be checked
582      * @return <code>true</code> if interface is implemented by class,
583      * <code>false</code> if not
584      * @throws JiBXException if configuration error
585      */

586
587     public boolean isImplements(String JavaDoc sig) throws JiBXException {
588         String JavaDoc[] sigs = getInstanceSigs();
589         for (int i = 0; i < sigs.length; i++) {
590             if (sig.equals(sigs[i])) {
591                 return true;
592             }
593         }
594         return false;
595     }
596
597     /**
598      * Check if another class is a superclass of this one.
599      *
600      * @param name of superclass to be checked
601      * @return <code>true</code> if named class is a superclass of this one,
602      * <code>false</code> if not
603      * @throws JiBXException if configuration error
604      */

605
606     public boolean isSuperclass(String JavaDoc name) throws JiBXException {
607         ClassFile cur = this;
608         while (cur != null) {
609             if (cur.getName().equals(name)) {
610                 return true;
611             } else {
612                 cur = cur.getSuperFile();
613             }
614         }
615         return false;
616     }
617
618     /**
619      * Get internal information for field. This can only be used with
620      * existing classes, and only checks for fields that are actually members
621      * of the class (not superclasses).
622      *
623      * @param name field name
624      * @return field information, or <code>null</code> if field not found
625      */

626
627     protected Field getDefinedField(String JavaDoc name) {
628
629         // check for match to field name defined in class
630
Field[] fields = m_curClass.getFields();
631         for (int i = 0; i < fields.length; i++) {
632             if (fields[i].getName().equals(name)) {
633                 return fields[i];
634             }
635         }
636         return null;
637     }
638
639     /**
640      * Get internal information for field. This can only be used with existing
641      * classes. If the field is not found directly, superclasses are checked for
642      * inherited fields matching the supplied name.
643      *
644      * @param name field name
645      * @return field information, or <code>null</code> if field not found
646      */

647
648     protected Field getAccessibleField(String JavaDoc name) {
649         
650         // always return not found for unloadable class
651
if (m_curClass == null) {
652             return null;
653         } else {
654     
655             // check for match to field name defined in class
656
Field field = getDefinedField(name);
657             if (field == null) {
658         
659                 // try match to field inherited from superclass
660
if (m_superClass != null) {
661                     field = m_superClass.getAccessibleField(name);
662                     if (field != null && (!m_isSamePackage ||
663                         field.isPrivate()) && !field.isPublic() &&
664                         !field.isProtected()) {
665                         field = null;
666                     }
667                 }
668                 
669             }
670             return field;
671         }
672     }
673
674     /**
675      * Get information for field. This can only be used with existing classes,
676      * and only checks for fields that are actually members of the class (not
677      * superclasses).
678      *
679      * @param name field name
680      * @return field information, or <code>null</code> if field not found
681      */

682
683     public ClassItem getDirectField(String JavaDoc name) {
684         Field field = getAccessibleField(name);
685         if (field == null) {
686             return null;
687         } else {
688             ClassItem item = (ClassItem)m_itemMap.get(field);
689             if (item == null) {
690                 item = new ClassItem(name, this, field);
691                 m_itemMap.put(field, item);
692             }
693             return item;
694         }
695     }
696
697     /**
698      * Get information for field. This can only be used with existing classes.
699      * If the field is not found directly, superclasses are checked for
700      * inherited fields matching the supplied name.
701      *
702      * @param name field name
703      * @return field information
704      * @throws JiBXException if field not found
705      */

706
707     public ClassItem getField(String JavaDoc name) throws JiBXException {
708         Field field = getAccessibleField(name);
709         if (field == null) {
710             throw new JiBXException("Field " + name +
711                 " not found in class " + m_name);
712         } else {
713             ClassItem item = (ClassItem)m_itemMap.get(field);
714             if (item == null) {
715                 item = new ClassItem(name, this, field);
716                 m_itemMap.put(field, item);
717             }
718             return item;
719         }
720     }
721
722     /**
723      * Get internal information for method without respect to potential trailing
724      * arguments or return value. This can only be used with existing classes.
725      * If the method is not found directly, superclasses are checked for
726      * inherited methods matching the supplied name. This compares the supplied
727      * partial signature against the actual method signature, and considers it
728      * a match if the actual sigature starts with the supplied signature..
729      *
730      * @param name method name
731      * @param sig partial method signature to be matched
732      * @return method information, or <code>null</code> if method not found
733      */

734
735     protected Method getAccessibleMethod(String JavaDoc name, String JavaDoc sig) {
736         
737         // only check loadable classes
738
if (m_curClass != null) {
739     
740             // check for match to method defined in class
741
Method[] methods = m_curClass.getMethods();
742             for (int i = 0; i < methods.length; i++) {
743                 Method method = methods[i];
744                 if (method.getName().equals(name)) {
745                     if (method.getSignature().startsWith(sig)) {
746                         return method;
747                     }
748                 }
749             }
750     
751             // try match to method inherited from superclass
752
if (m_superClass != null) {
753                 Method method = m_superClass.getAccessibleMethod(name, sig);
754                 if (method != null && ((m_isSamePackage &&
755                     !method.isPrivate()) || method.isPublic() ||
756                     method.isProtected())) {
757                     return method;
758                 }
759             }
760             
761         }
762         return null;
763     }
764
765     /**
766      * Get information for method without respect to potential trailing
767      * arguments or return value. This can only be used with existing classes.
768      * If the method is not found directly, superclasses are checked for
769      * inherited methods matching the supplied name. This compares the supplied
770      * partial signature against the actual method signature, and considers it
771      * a match if the actual sigature starts with the supplied signature..
772      *
773      * @param name method name
774      * @param sig partial method signature to be matched
775      * @return method information, or <code>null</code> if method not found
776      */

777
778     public ClassItem getMethod(String JavaDoc name, String JavaDoc sig) {
779         Method method = getAccessibleMethod(name, sig);
780         if (method == null) {
781             return null;
782         } else {
783             ClassItem item = (ClassItem)m_itemMap.get(method);
784             if (item == null) {
785                 item = new ClassItem(name, this, method);
786                 m_itemMap.put(method, item);
787             }
788             return item;
789         }
790     }
791
792     /**
793      * Get information for method matching one of several possible signatures.
794      * This can only be used with existing classes. If a match is not found
795      * directly, superclasses are checked for inherited methods matching the
796      * supplied name and signatures. The signature variations are checked in
797      * the order supplied.
798      *
799      * @param name method name
800      * @param sigs possible signatures for method (including return type)
801      * @return method information, or <code>null</code> if method not found
802      */

803
804     public ClassItem getMethod(String JavaDoc name, String JavaDoc[] sigs) {
805         Method method = null;
806         for (int i = 0; method == null && i < sigs.length; i++) {
807             method = getAccessibleMethod(name, sigs[i]);
808         }
809         if (method == null) {
810             return null;
811         } else {
812             ClassItem item = (ClassItem)m_itemMap.get(method);
813             if (item == null) {
814                 item = new ClassItem(name, this, method);
815                 m_itemMap.put(method, item);
816             }
817             return item;
818         }
819     }
820     
821     /**
822      * Check for match to specified access level. This treats a field or method
823      * as matching if the access level is the same as or more open than the
824      * required level.
825      *
826      * @param item information for field or method to be checked
827      * @param access required access level for match
828      * @return <code>true</code> if access level match, <code>false</code> if
829      * not
830      */

831     
832     private static boolean matchAccess(FieldOrMethod item, int access) {
833         if (item.isPublic()) {
834             return true;
835         } else if (item.isProtected()) {
836             return access <= PROTECTED_ACCESS;
837         } else if (item.isPrivate()) {
838             return access == PRIVATE_ACCESS;
839         } else {
840             return access <= PACKAGE_ACCESS;
841         }
842     }
843     
844     /**
845      * Check if one type is assignment compatible with another type. This is an
846      * ugly replacement for apparently broken BCEL code.
847      *
848      * @param have type being checked
849      * @param need type needed
850      * @return
851      */

852     private static boolean isAssignmentCompatible(Type have, Type need) {
853         if (have.equals(need)) {
854             return true;
855         } else {
856             try {
857                 return ClassItem.isAssignable(have.toString(), need.toString());
858             } catch (JiBXException e) {
859                 throw new IllegalStateException JavaDoc
860                     ("Internal error: Unable to access data for " +
861                     have.toString() + " or " + need.toString() + ":\n" +
862                     e.getMessage());
863             }
864         }
865     }
866
867     /**
868      * Get information for best matching method. This tries to find a method
869      * which matches the specified name, return type, and argument types. If an
870      * exact match is not found it looks for a method with a return type that
871      * is extended or implemented by the specified type and arguments that are
872      * extended or implemented by the specified types. This can only be used
873      * with existing classes. If the method is not found directly, superclasses
874      * are checked for inherited methods.
875      *
876      * @param name method name
877      * @param access access level required for matching methods
878      * @param ret return value type (<code>null</code> if indeterminant)
879      * @param args argument value types
880      * @return method information, or <code>null</code> if method not found
881      */

882
883     private Method getBestAccessibleMethod(String JavaDoc name, int access, Type ret,
884         Type[] args) {
885         
886         // just fail for classes that aren't loadable
887
if (m_curClass == null) {
888             return null;
889         }
890
891         // check for match to method defined in class
892
Method[] methods = m_curClass.getMethods();
893         Method best = null;
894         int diff = Integer.MAX_VALUE;
895         for (int i = 0; i < methods.length; i++) {
896             Method method = methods[i];
897             if (method.getName().equals(name) && matchAccess(method, access)) {
898                 
899                 // make sure the return type is compatible
900
boolean match = true;
901                 int ndiff = 0;
902                 if (ret != null) {
903                     Type type = method.getReturnType();
904                     match = isAssignmentCompatible(ret, type);
905                 }
906                 if (match) {
907                     
908                     // check closeness of argument types
909
Type[] types = method.getArgumentTypes();
910                     if (args.length == types.length) {
911                         for (int j = 0; j < args.length; j++) {
912                             Type type = types[j];
913                             Type arg = args[j];
914                             if (!type.equals(arg)) {
915                                 ndiff++;
916                                 match = isAssignmentCompatible(arg, type);
917                                 if (!match) {
918                                     break;
919                                 }
920                             }
921                         }
922                     } else {
923                         match = false;
924                     }
925                 }
926                 if (match && ndiff < diff) {
927                     best = method;
928                 }
929             }
930         }
931         if (best != null) {
932             return best;
933         }
934         
935         // try methods inherited from superclass if no match found
936
if (m_superClass != null) {
937             if (access < PROTECTED_ACCESS) {
938                 if (m_isSamePackage) {
939                     access = PACKAGE_ACCESS;
940                 } else {
941                     access = PROTECTED_ACCESS;
942                 }
943             }
944             return m_superClass.getBestAccessibleMethod(name, access, ret, args);
945         } else {
946             return null;
947         }
948     }
949
950     /**
951      * Get information for best matching method. This tries to find a method
952      * which matches the specified name, return type, and argument types. If an
953      * exact match is not found it looks for a method with a return type that
954      * is extended or implemented by the specified type and arguments that are
955      * extended or implemented by the specified types. This can only be used
956      * with existing classes. If the method is not found directly, superclasses
957      * are checked for inherited methods.
958      *
959      * @param name method name
960      * @param ret return value type (<code>null</code> if indeterminant)
961      * @param args argument value types
962      * @return method information, or <code>null</code> if method not found
963      */

964
965     public ClassItem getBestMethod(String JavaDoc name, String JavaDoc ret, String JavaDoc[] args) {
966         Type rtype = null;
967         if (ret != null) {
968             rtype = ClassItem.typeFromName(ret);
969         }
970         Type[] atypes = new Type[args.length];
971         for (int i = 0; i < args.length; i++) {
972             atypes[i] = ClassItem.typeFromName(args[i]);
973         }
974         Method method =
975             getBestAccessibleMethod(name, PRIVATE_ACCESS, rtype, atypes);
976         if (method == null) {
977             return null;
978         }
979         ClassItem item = (ClassItem)m_itemMap.get(method);
980         if (item == null) {
981             item = new ClassItem(name, this, method);
982             m_itemMap.put(method, item);
983         }
984         return item;
985     }
986
987     /**
988      * Get information for initializer. This can only be used with existing
989      * classes. Only the class itself is checked for an initializer matching
990      * the argument list signature.
991      *
992      * @param sig encoded argument list signature
993      * @return method information, or <code>null</code> if method not found
994      */

995
996     public ClassItem getInitializerMethod(String JavaDoc sig) {
997         
998         // only check if loadable class
999
if (m_curClass != null) {
1000    
1001            // check for match to method defined in class
1002
Method[] methods = m_curClass.getMethods();
1003            for (int i = 0; i < methods.length; i++) {
1004                Method method = methods[i];
1005                if (method.getName().equals("<init>")) {
1006                    String JavaDoc sign = method.getSignature();
1007                    if (method.getSignature().startsWith(sig)) {
1008                        ClassItem item = (ClassItem)m_itemMap.get(method);
1009                        if (item == null) {
1010                            item = new ClassItem("<init>", this, method);
1011                            m_itemMap.put(method, item);
1012                        }
1013                        return item;
1014                    }
1015                }
1016            }
1017            
1018        }
1019        return null;
1020    }
1021
1022    /**
1023     * Get information for static method without respect to return value. This
1024     * can only be used with existing classes. Only the class itself is checked
1025     * for a method matching the supplied name and argument list signature.
1026     *
1027     * @param name method name
1028     * @param sig encoded argument list signature
1029     * @return method information, or <code>null</code> if method not found
1030     */

1031
1032    public ClassItem getStaticMethod(String JavaDoc name, String JavaDoc sig) {
1033        
1034        // only check if loadable class
1035
if (m_curClass != null) {
1036    
1037            // check for match to method defined in class
1038
Method[] methods = m_curClass.getMethods();
1039            for (int i = 0; i < methods.length; i++) {
1040                Method method = methods[i];
1041                if (method.getName().equals(name) && method.isStatic()) {
1042                    if (method.getSignature().startsWith(sig)) {
1043                        ClassItem item = (ClassItem)m_itemMap.get(method);
1044                        if (item == null) {
1045                            item = new ClassItem(name, this, method);
1046                            m_itemMap.put(method, item);
1047                        }
1048                        return item;
1049                    }
1050                }
1051            }
1052            
1053        }
1054        return null;
1055    }
1056
1057    /**
1058     * Get all binding methods currently defined in class. Binding methods are
1059     * generally identified by a supplied prefix, but additional methods
1060     * can be specified the the combination of exact name and signature. This
1061     * is a little kludgy, but necessary to handle the "marshal" method added
1062     * to mapped classes.
1063     *
1064     * @param prefix identifying prefix for binding methods
1065     * @param matches pairs of method name and signature to be matched as
1066     * exceptions to the prefix matching
1067     * @return existing binding methods
1068     */

1069
1070    public ExistingMethod[] getBindingMethods(String JavaDoc prefix, String JavaDoc[] matches) {
1071        
1072        // return empty array if newly created class or unloadable class
1073
if (m_curClass == null) {
1074            return EMPTY_METHOD_ARRAY;
1075        }
1076
1077        // check for binding methods defined in class
1078
Method[] methods = m_curClass.getMethods();
1079        int count = 0;
1080        for (int i = 0; i < methods.length; i++) {
1081            Method method = methods[i];
1082            String JavaDoc name = method.getName();
1083            if (name.startsWith(prefix)) {
1084                count++;
1085            } else {
1086                String JavaDoc sig = method.getSignature();
1087                for (int j = 0; j < matches.length; j += 2) {
1088                    if (name.equals(matches[j]) && sig.equals(matches[j+1])) {
1089                        count++;
1090                        break;
1091                    }
1092                }
1093            }
1094        }
1095        
1096        // generate array of methods found
1097
if (count == 0) {
1098            return EMPTY_METHOD_ARRAY;
1099        } else {
1100            ExistingMethod[] exists = new ExistingMethod[count];
1101            int fill = 0;
1102            for (int i = 0; i < methods.length; i++) {
1103                Method method = methods[i];
1104                String JavaDoc name = method.getName();
1105                boolean match = name.startsWith(prefix);
1106                if (!match) {
1107                    String JavaDoc sig = method.getSignature();
1108                    for (int j = 0; j < matches.length; j += 2) {
1109                        if (name.equals(matches[j]) &&
1110                            sig.equals(matches[j+1])) {
1111                            match = true;
1112                            break;
1113                        }
1114                    }
1115                }
1116                if (match) {
1117                    ClassItem item = (ClassItem)m_itemMap.get(method);
1118                    if (item == null) {
1119                        item = new ClassItem(name, this, method);
1120                        m_itemMap.put(method, item);
1121                    }
1122                    exists[fill++] = new ExistingMethod(method, item, this);
1123                }
1124            }
1125            return exists;
1126        }
1127    }
1128
1129    /**
1130     * Check accessible method. Check if a field or method in another class is
1131     * accessible from within this class.
1132     *
1133     * @param item field or method information
1134     * @return <code>true</code> if accessible, <code>false</code> if not
1135     */

1136
1137    public boolean isAccessible(ClassItem item) {
1138        if (item.getClassFile() == this) {
1139            return true;
1140        } else {
1141            int access = item.getAccessFlags();
1142            if ((access & Constants.ACC_PUBLIC) != 0) {
1143                return true;
1144            } else if ((access & Constants.ACC_PRIVATE) != 0) {
1145                return false;
1146            } else if (getPackage().equals(item.getClassFile().getPackage())) {
1147                return true;
1148            } else if ((access & Constants.ACC_PROTECTED) != 0) {
1149                ClassFile target = item.getClassFile();
1150                ClassFile ancestor = this;
1151                while ((ancestor = ancestor.getSuperFile()) != null) {
1152                    if (ancestor == target) {
1153                        return true;
1154                    }
1155                }
1156                return false;
1157            } else {
1158                return false;
1159            }
1160        }
1161    }
1162
1163    /**
1164     * Get generator for modifying class.
1165     *
1166     * @return generator for class
1167     * @throws JiBXException if class not modifiable
1168     */

1169
1170    private ClassGen getClassGen() throws JiBXException {
1171        if (m_genClass == null) {
1172            if (m_isWritable) {
1173                m_genClass = new ClassGen(m_curClass);
1174                m_genPool = m_genClass.getConstantPool();
1175                m_instBuilder = new InstructionBuilder(m_genClass, m_genPool);
1176                m_isHashCurrent = false;
1177            } else {
1178                throw new JiBXException("Cannot modify class " + m_name);
1179            }
1180        }
1181        return m_genClass;
1182    }
1183
1184    /**
1185     * Get constant pool generator for modifying class.
1186     *
1187     * @return constant pool generator for class
1188     * @throws JiBXException if class not modifiable
1189     */

1190
1191    public ConstantPoolGen getConstPoolGen() throws JiBXException {
1192        if (m_genPool == null) {
1193            getClassGen();
1194        }
1195        return m_genPool;
1196    }
1197
1198    /**
1199     * Get instruction builder for modifying class.
1200     *
1201     * @return instruction builder for class
1202     * @throws JiBXException if class not modifiable
1203     */

1204
1205    public InstructionBuilder getInstructionBuilder() throws JiBXException {
1206        if (m_instBuilder == null) {
1207            getClassGen();
1208        }
1209        return m_instBuilder;
1210    }
1211
1212    /**
1213     * Add method to class.
1214     *
1215     * @param method method to be added
1216     * @return added method information
1217     * @throws JiBXException on error in adding method
1218     */

1219
1220    public ClassItem addMethod(Method method) throws JiBXException {
1221        getClassGen().addMethod(method);
1222        setModified();
1223        String JavaDoc mname = method.getName();
1224        if (m_suffixMap != null && isSuffixName(mname)) {
1225            m_suffixMap.put(mname, method);
1226        }
1227        return new ClassItem(mname, this, method);
1228    }
1229
1230    /**
1231     * Remove method from class.
1232     *
1233     * @param method method to be removed
1234     * @throws JiBXException on error in removing method
1235     */

1236
1237    public void removeMethod(Method method) throws JiBXException {
1238        getClassGen().removeMethod(method);
1239        setModified();
1240        String JavaDoc mname = method.getName();
1241        if (m_suffixMap != null && isSuffixName(mname)) {
1242            m_suffixMap.remove(mname);
1243        }
1244    }
1245
1246    /**
1247     * Add field to class with initial <code>String</code> value. If a field
1248     * with the same name already exists, it is overwritten.
1249     *
1250     * @param type fully qualified class name of field type
1251     * @param name field name
1252     * @param access access flags for field
1253     * @param init initial value for field
1254     * @return field information
1255     * @throws JiBXException if unable to add field
1256     */

1257
1258    public ClassItem addField(String JavaDoc type, String JavaDoc name, int access, String JavaDoc init)
1259        throws JiBXException {
1260        deleteField(name);
1261        FieldGen fgen = new FieldGen(access,
1262            Type.getType(Utility.getSignature(type)), name, getConstPoolGen());
1263        fgen.setInitValue(init);
1264        Field field = fgen.getField();
1265        getClassGen().addField(field);
1266        m_isModified = true;
1267        m_isHashCurrent = false;
1268        return new ClassItem(name, this, field);
1269    }
1270
1271    /**
1272     * Update class field with initial <code>String</code> value. If the field
1273     * already exists with the same characteristics it is left unchanged;
1274     * otherwise any existing field with the same name is overwritten.
1275     *
1276     * @param type fully qualified class name of field type
1277     * @param name field name
1278     * @param access access flags for field
1279     * @param init initial value for field
1280     * @return field information
1281     * @throws JiBXException if unable to add field
1282     */

1283
1284    public ClassItem updateField(String JavaDoc type, String JavaDoc name, int access,
1285        String JavaDoc init) throws JiBXException {
1286        
1287        // first check for match with existing field
1288
Field[] fields = m_curClass.getFields();
1289        for (int i = 0; i < fields.length; i++) {
1290            Field field = fields[i];
1291            if (field.getName().equals(name) &&
1292                field.getAccessFlags() == access) {
1293                String JavaDoc sig = field.getSignature();
1294                if (type.equals(Utility.signatureToString(sig, false))) {
1295                    ConstantValue cval = field.getConstantValue();
1296                    if (cval != null) {
1297                        int index = cval.getConstantValueIndex();
1298                        ConstantPool cp = m_curClass.getConstantPool();
1299                        Constant cnst = cp.getConstant(index);
1300                        if (cnst instanceof ConstantString) {
1301                            Object JavaDoc value = ((ConstantString)cnst).
1302                                getConstantValue(cp);
1303                            if (init.equals(value)) {
1304                                return new ClassItem(name,this, field);
1305                            }
1306                        }
1307                    }
1308                }
1309            }
1310        }
1311        
1312        // no exact match, so replace any existing field with same name
1313
deleteField(name);
1314        FieldGen fgen = new FieldGen(access,
1315            Type.getType(Utility.getSignature(type)), name, getConstPoolGen());
1316        fgen.setInitValue(init);
1317        Field field = fgen.getField();
1318        getClassGen().addField(field);
1319        m_isModified = true;
1320        m_isHashCurrent = false;
1321        return new ClassItem(name, this, field);
1322    }
1323
1324    /**
1325     * Add field to class without initialization. If a field with the same name
1326     * already exists, it is overwritten.
1327     *
1328     * @param type fully qualified class name of field type
1329     * @param name field name
1330     * @param access access flags for field
1331     * @return field information
1332     * @throws JiBXException if unable to add field
1333     */

1334
1335    public ClassItem addField(String JavaDoc type, String JavaDoc name, int access)
1336        throws JiBXException {
1337        deleteField(name);
1338        FieldGen fgen = new FieldGen(access,
1339            Type.getType(Utility.getSignature(type)), name, getConstPoolGen());
1340        Field field = fgen.getField();
1341        getClassGen().addField(field);
1342        m_isModified = true;
1343        m_isHashCurrent = false;
1344        return new ClassItem(name, this, field);
1345    }
1346
1347    /**
1348     * Add private field to class without initialization. If a field
1349     * with the same name already exists, it is overwritten.
1350     *
1351     * @param type fully qualified class name of field type
1352     * @param name field name
1353     * @return field information
1354     * @throws JiBXException if unable to add field
1355     */

1356
1357    public ClassItem addPrivateField(String JavaDoc type, String JavaDoc name)
1358        throws JiBXException {
1359        return addField(type, name, PRIVATEFIELD_ACCESS);
1360    }
1361
1362    /**
1363     * Add default constructor to a class. Just calls the default constructor
1364     * for the parent class (which must exist).
1365     *
1366     * @return constructor information
1367     * @throws JiBXException if unable to add constructor
1368     */

1369
1370    public ClassItem addDefaultConstructor() throws JiBXException {
1371    
1372        // add the public constructor method
1373
ExceptionMethodBuilder mb = new ExceptionMethodBuilder("<init>",
1374            Type.VOID, new Type[0], this, Constants.ACC_PUBLIC);
1375    
1376        // call the superclass constructor
1377
mb.appendLoadLocal(0);
1378        mb.appendCallInit(m_superClass.getName(), "()V");
1379        
1380        // finish with return
1381
mb.appendReturn();
1382        mb.codeComplete(false);
1383        return mb.addMethod();
1384    }
1385
1386    /**
1387     * Check if a method name matches the pattern for a generated unique suffix.
1388     *
1389     * @param name method name to be checked
1390     * @return <code>true</code> if name matches suffix pattern,
1391     * <code>false</code> if not
1392     */

1393
1394    private static boolean isSuffixName(String JavaDoc name) {
1395        int last = name.length() - 1;
1396        for (int i = last; i > 0; i--) {
1397            char chr = name.charAt(i);
1398            if (chr == '_') {
1399                return i < last;
1400            } else if (!Character.isDigit(chr)) {
1401                break;
1402            }
1403        }
1404        return false;
1405    }
1406
1407    /**
1408     * Make method name unique with generated suffix. The suffixed method name
1409     * is tracked so that it will not be used again.
1410     *
1411     * @param name base name before suffix is appended
1412     * @return name with unique suffix appended
1413     */

1414
1415    public String JavaDoc makeUniqueMethodName(String JavaDoc name) {
1416        
1417        // check if map creation is needed
1418
if (m_suffixMap == null) {
1419            m_suffixMap = new HashMap JavaDoc();
1420            if (m_curClass != null) {
1421                Method[] methods = m_curClass.getMethods();
1422                for (int i = 0; i < methods.length; i++) {
1423                    Method method = methods[i];
1424                    String JavaDoc mname = method.getName();
1425                    if (isSuffixName(mname)) {
1426                        m_suffixMap.put(mname, method);
1427                    }
1428                }
1429            }
1430        }
1431        
1432        // check if inheritance depth is needed
1433
if (m_inheritDepth == 0) {
1434            ClassFile cf = this;
1435            while ((cf = cf.getSuperFile()) != null) {
1436                m_inheritDepth++;
1437            }
1438        }
1439        
1440        // generate suffix to make name unique, trying low values first
1441
while (true) {
1442            String JavaDoc uname = name + '_' + m_inheritDepth + '_' + m_uniqueIndex;
1443            if (m_suffixMap.get(uname) == null) {
1444                return uname;
1445            } else {
1446                m_uniqueIndex++;
1447            }
1448        }
1449    }
1450
1451    /**
1452     * Delete field from class.
1453     *
1454     * @param name field name
1455     * @return <code>true</code> if field was present, <code>false</code> if not
1456     * @throws JiBXException if unable to delete field
1457     */

1458
1459    public boolean deleteField(String JavaDoc name) throws JiBXException {
1460        ClassGen cg = getClassGen();
1461        Field field = cg.containsField(name);
1462        if (field == null) {
1463            return false;
1464        } else {
1465            cg.removeField(field);
1466            m_isModified = true;
1467            m_isHashCurrent = false;
1468            return true;
1469        }
1470    }
1471
1472    /**
1473     * Get use count for class.
1474     *
1475     * @return use count for this class
1476     */

1477
1478    public int getUseCount() {
1479        return m_useCount;
1480    }
1481
1482    /**
1483     * Increment use count for class.
1484     *
1485     * @return use count (after increment)
1486     */

1487
1488    public int incrementUseCount() {
1489        return ++m_useCount;
1490    }
1491
1492    /**
1493     * Check if class has been modified.
1494     *
1495     * @return <code>true</code> if class is modified, <code>false</code> if not
1496     */

1497
1498    public boolean isModified() {
1499        return m_isModified;
1500    }
1501
1502    /**
1503     * Set class modified flag.
1504     */

1505
1506    public void setModified() {
1507        m_isModified = true;
1508    }
1509
1510    /**
1511     * Check if class is in complete state.
1512     *
1513     * @return <code>true</code> if class is complete, <code>false</code> if not
1514     */

1515
1516    public boolean isComplete() {
1517        return m_genClass == null;
1518    }
1519
1520    /**
1521     * Computes a hash code based on characteristics of the class. The
1522     * characteristics used in computing the hash code include the base class,
1523     * implemented interfaces, method names, field names, and package, but not
1524     * the actual class name or signature. The current static version of the
1525     * class is used for this computation, so if the class is being modified
1526     * {@link #codeComplete} should be called before this method. Note that this
1527     * is designed for use with the classes generated by JiBX, and is not
1528     * necessarily a good model for general usage.
1529     *
1530     * @return computed hash code value
1531     */

1532
1533    protected int computeHashCode() {
1534        
1535        // start with basic characteristics of class
1536
int hash = getPackage().hashCode();
1537        ClassFile sfile = getSuperFile();
1538        if (sfile != null) {
1539            hash += sfile.getName().hashCode();
1540        }
1541        String JavaDoc[] intfs = getInterfaces();
1542        for (int i = 0; i < intfs.length; i++) {
1543            hash += intfs[i].hashCode();
1544        }
1545        hash += m_curClass.getAccessFlags();
1546        
1547        // include field and method names
1548
Field[] fields = m_curClass.getFields();
1549        for (int i = 0; i < fields.length; i++) {
1550            hash = hash * 49 + fields[i].getName().hashCode();
1551        }
1552        Method[] methods = m_curClass.getMethods();
1553        for (int i = 0; i < methods.length; i++) {
1554            hash = hash * 49 + methods[i].getName().hashCode();
1555        }
1556        
1557        // finish with constant table simple values (except name and signature)
1558
Constant[] cnsts = m_curClass.getConstantPool().getConstantPool();
1559        for (int i = m_curClass.getClassNameIndex()+1; i < cnsts.length; i++) {
1560            Constant cnst = cnsts[i];
1561            if (cnst != null) {
1562                int value = 0;
1563                switch (cnst.getTag()) {
1564                    case Constants.CONSTANT_Double:
1565                        value = (int)Double.doubleToRawLongBits
1566                            (((ConstantDouble)cnst).getBytes());
1567                        break;
1568                    case Constants.CONSTANT_Float:
1569                        value = Float.floatToRawIntBits
1570                            (((ConstantFloat)cnst).getBytes());
1571                        break;
1572                    case Constants.CONSTANT_Integer:
1573                        value = ((ConstantInteger)cnst).getBytes();
1574                        break;
1575                    case Constants.CONSTANT_Long:
1576                        value = (int)((ConstantLong)cnst).getBytes();
1577                        break;
1578                    case Constants.CONSTANT_Utf8:
1579                        String JavaDoc text = ((ConstantUtf8)cnst).getBytes();
1580                        if (!text.equals(m_signature)) {
1581                            value = text.hashCode();
1582                        }
1583                        break;
1584                    default:
1585                        break;
1586                }
1587                hash = hash * 49 + value;
1588            }
1589        }
1590// System.out.println("Hashed " + m_name + " to value " + hash);
1591
return hash;
1592    }
1593
1594    /**
1595     * Finalize current modified state of class. This converts the modified
1596     * class state into a static form, then computes a hash code based on
1597     * characteristics of the class. If the class has not been modified it
1598     * just computes the hash code.
1599     */

1600
1601    public void codeComplete() {
1602        if (m_genClass != null) {
1603            m_curClass = m_genClass.getJavaClass();
1604            m_interfaces = m_curClass.getInterfaceNames();
1605            m_genClass = null;
1606        }
1607    }
1608
1609    /**
1610     * Get hash code. This is based on most characteristics of the class,
1611     * including the actual methods, but excluding the class name. It is only
1612     * valid after the {@link #codeComplete} method is called.
1613     *
1614     * @return hash code based on code sequence
1615     */

1616
1617    public int hashCode() {
1618        if (!m_isHashCurrent) {
1619            if (m_genClass != null) {
1620                throw new IllegalStateException JavaDoc
1621                    ("Class still being constructed");
1622            }
1623            m_hashCode = computeHashCode();
1624            m_isHashCurrent = true;
1625        }
1626        return m_hashCode;
1627    }
1628
1629    /**
1630     * Compare two field or method items to see if they're equal. This handles
1631     * only the comparisons that apply to both fields and methods. It does not
1632     * include comparing access flags, since these may be different due to
1633     * access requirements.
1634     *
1635     * @param a first field or method item
1636     * @param b second field or method item
1637     * @return <code>true</code> if the equal, <code>false</code> if not
1638     */

1639
1640    public static boolean equalFieldOrMethods(FieldOrMethod a,
1641        FieldOrMethod b) {
1642        return a.getName().equals(b.getName()) &&
1643            a.getSignature().equals(b.getSignature());
1644    }
1645
1646    /**
1647     * Compare two methods to see if they're equal. This checks only the details
1648     * of the exception handling and actual code, not the name or signature.
1649     *
1650     * @param a first method
1651     * @param b second method
1652     * @return <code>true</code> if the equal, <code>false</code> if not
1653     */

1654
1655    public static boolean equalMethods(Method a, Method b) {
1656            
1657        // check the exceptions thrown by the method
1658
ExceptionTable etaba = a.getExceptionTable();
1659        ExceptionTable etabb = b.getExceptionTable();
1660        if (etaba != null && etabb != null) {
1661            String JavaDoc[] aexcepts = etaba.getExceptionNames();
1662            String JavaDoc[] bexcepts = etabb.getExceptionNames();
1663            if (!Arrays.equals(aexcepts, bexcepts)) {
1664                return false;
1665            }
1666        } else if (etaba != null || etabb != null) {
1667            return false;
1668        }
1669        
1670        // compare the exception handling details
1671
Code acode = a.getCode();
1672        Code bcode = b.getCode();
1673        CodeException[] acexs = acode.getExceptionTable();
1674        CodeException[] bcexs = bcode.getExceptionTable();
1675        if (acexs.length == bcexs.length) {
1676            for (int i = 0; i < acexs.length; i++) {
1677                CodeException acex = acexs[i];
1678                CodeException bcex = bcexs[i];
1679                if (acex.getCatchType() != bcex.getCatchType() ||
1680                    acex.getStartPC() != bcex.getStartPC() ||
1681                    acex.getEndPC() != bcex.getEndPC() ||
1682                    acex.getHandlerPC() != bcex.getHandlerPC()) {
1683                    return false;
1684                }
1685            }
1686        }
1687        
1688        // finally compare the actual byte codes
1689
return Arrays.equals(acode.getCode(), bcode.getCode());
1690    }
1691
1692    /**
1693     * Check if objects are equal. Compares first based on hash code, then on
1694     * the actual contents of the class, including package, implemented
1695     * interfaces, superclass, methods, and fields (but not the actual class
1696     * name). It is only valid after the {@link #codeComplete} method is called.
1697     *
1698     * @return <code>true</code> if equal objects, <code>false</code> if not
1699     */

1700
1701    public boolean equals(Object JavaDoc obj) {
1702        if (obj instanceof ClassFile && obj.hashCode() == hashCode()) {
1703            
1704            // check basic details of the classes
1705
ClassFile comp = (ClassFile)obj;
1706            if (!org.jibx.runtime.Utility.isEqual(getPackage(),
1707                comp.getPackage()) || getSuperFile() != comp.getSuperFile() ||
1708                !Arrays.equals(getInterfaces(), comp.getInterfaces())) {
1709                return false;
1710            }
1711            JavaClass tjc = m_curClass;
1712            JavaClass cjc = comp.m_curClass;
1713            if (tjc.getAccessFlags() != cjc.getAccessFlags()) {
1714                return false;
1715            }
1716            
1717            // compare the defined fields
1718
Field[] tfields = tjc.getFields();
1719            Field[] cfields = cjc.getFields();
1720            if (tfields.length != cfields.length) {
1721                return false;
1722            }
1723            for (int i = 0; i < tfields.length; i++) {
1724                if (!equalFieldOrMethods(tfields[i], cfields[i])) {
1725                    return false;
1726                }
1727            }
1728            
1729            // compare the defined methods
1730
Method[] tmethods = tjc.getMethods();
1731            Method[] cmethods = cjc.getMethods();
1732            if (tmethods.length != cmethods.length) {
1733                return false;
1734            }
1735            for (int i = 0; i < tmethods.length; i++) {
1736                Method tmethod = tmethods[i];
1737                Method cmethod = cmethods[i];
1738                if (!equalFieldOrMethods(tmethod, cmethod) ||
1739                    !equalMethods(tmethod, cmethod)) {
1740                    return false;
1741                }
1742            }
1743        
1744            // finish with constant table values (correcting name and signature)
1745
Constant[] tcnsts = tjc.getConstantPool().getConstantPool();
1746            Constant[] ccnsts = cjc.getConstantPool().getConstantPool();
1747            if (tcnsts.length != ccnsts.length) {
1748                return false;
1749            }
1750            for (int i = tjc.getClassNameIndex()+1; i < tcnsts.length; i++) {
1751                Constant tcnst = tcnsts[i];
1752                Constant ccnst = ccnsts[i];
1753                if (tcnst != null && ccnst != null) {
1754                    int tag = tcnst.getTag();
1755                    if (tag != ccnst.getTag()) {
1756                        return false;
1757                    }
1758                    boolean equal = true;
1759                    switch (tag) {
1760                        case Constants.CONSTANT_Double:
1761                            equal = ((ConstantDouble)tcnst).getBytes() ==
1762                                ((ConstantDouble)ccnst).getBytes();
1763                            break;
1764                        case Constants.CONSTANT_Float:
1765                            equal = ((ConstantFloat)tcnst).getBytes() ==
1766                                ((ConstantFloat)ccnst).getBytes();
1767                            break;
1768                        case Constants.CONSTANT_Integer:
1769                            equal = ((ConstantInteger)tcnst).getBytes() ==
1770                                ((ConstantInteger)ccnst).getBytes();
1771                            break;
1772                        case Constants.CONSTANT_Long:
1773                            equal = ((ConstantLong)tcnst).getBytes() ==
1774                                ((ConstantLong)ccnst).getBytes();
1775                            break;
1776                        case Constants.CONSTANT_Utf8:
1777                            String JavaDoc ttext = ((ConstantUtf8)tcnst).getBytes();
1778                            String JavaDoc ctext = ((ConstantUtf8)ccnst).getBytes();
1779                            if (ttext.equals(m_signature)) {
1780                                equal = ctext.equals(comp.m_signature);
1781                            } else {
1782                                equal = ttext.equals(ctext);
1783                            }
1784                            break;
1785                        default:
1786                            break;
1787                    }
1788                    if (!equal) {
1789                        return false;
1790                    }
1791                } else if (tcnst != null || ccnst != null) {
1792                    return false;
1793                }
1794            }
1795            
1796            // if nothing failed, the classes are equal
1797
return true;
1798            
1799        } else {
1800            return false;
1801        }
1802    }
1803
1804    /**
1805     * Delete class file information. Deletes the class file for this class,
1806     * if it exists. Does nothing if no class file has been generated.
1807     *
1808     * @param os output stream for writing modified class
1809     * @throws IOException if error writing to file
1810     */

1811
1812    public void delete() throws IOException JavaDoc {
1813        if (m_file.exists()) {
1814            m_file.delete();
1815        }
1816    }
1817
1818    /**
1819     * Write out modified class information. Writes the modified class file to
1820     * an output stream.
1821     *
1822     * @param os output stream for writing modified class
1823     * @throws IOException if error writing to file
1824     */

1825
1826    public void writeFile(OutputStream JavaDoc os) throws IOException JavaDoc {
1827        codeComplete();
1828        m_curClass.dump(os);
1829        os.close();
1830    }
1831
1832    /**
1833     * Write out modified class information. Writes the modified class file
1834     * back out to the original file. If the class file has not been modified,
1835     * the original file is kept unchanged.
1836     *
1837     * @throws IOException if error writing to file
1838     */

1839
1840    public void writeFile() throws IOException JavaDoc {
1841        if (m_isModified) {
1842            OutputStream JavaDoc os = new FileOutputStream JavaDoc(m_file);
1843            writeFile(os);
1844        }
1845    }
1846
1847    /**
1848     * Derive generated class name. This generates a JiBX class name from the
1849     * name of this class, using the supplied prefix and suffix information. The
1850     * derived class name is always in the same package as this class.
1851     *
1852     * @param prefix generated class name prefix
1853     * @param suffix generated class name suffix
1854     * @return derived class name
1855     */

1856
1857    public String JavaDoc deriveClassName(String JavaDoc prefix, String JavaDoc suffix) {
1858        String JavaDoc pack = "";
1859        String JavaDoc tname = m_name;
1860        int split = tname.lastIndexOf('.');
1861        if (split >= 0) {
1862            pack = tname.substring(0, split+1);
1863            tname = tname.substring(split+1);
1864        }
1865        return pack + prefix + tname + suffix;
1866    }
1867    
1868    /**
1869     * Set class paths to be searched.
1870     *
1871     * @param paths ordered set of paths to be searched for class files
1872     */

1873    
1874    public static void setPaths(String JavaDoc[] paths) {
1875        
1876        // create full path string with separators for BCEL loader
1877
StringBuffer JavaDoc full = new StringBuffer JavaDoc();
1878        for (int i = 0; i < paths.length; i++) {
1879            if (i > 0) {
1880                full.append(File.pathSeparatorChar);
1881            }
1882            full.append(paths[i]);
1883        }
1884        s_loader = new ClassPath(full.toString());
1885        
1886        // create direct classloader for access to classes during binding
1887
URL JavaDoc[] urls = new URL JavaDoc[paths.length];
1888        try {
1889            
1890            // generate array of component file or directory URLs
1891
for (int i = 0; i < urls.length; i++) {
1892                
1893                // append trailing slash on directory paths
1894
String JavaDoc path = paths[i];
1895                int mark = path.lastIndexOf('/');
1896                if (path.indexOf('.', mark) < 0) {
1897                    path = path + '/';
1898                }
1899                urls[i] = new URL JavaDoc("file:" + path);
1900            }
1901            
1902            // initialize classloader with full array of paths
1903
s_directLoader = new URLClassLoader JavaDoc(urls);
1904            
1905        } catch (MalformedURLException JavaDoc ex) {
1906            throw new IllegalArgumentException JavaDoc
1907                ("Error initializing classloading: " + ex.getMessage());
1908        }
1909    }
1910
1911    /**
1912     * Try loading class from classpath.
1913     *
1914     * @param name fully qualified name of class to be loaded
1915     * @return loaded class, or <code>null</code> if not found
1916     */

1917
1918    public static Class JavaDoc loadClass(String JavaDoc name) {
1919        try {
1920            return s_directLoader.loadClass(name);
1921        } catch (ClassNotFoundException JavaDoc ex) {
1922            return null;
1923        }
1924    }
1925}
Popular Tags