KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jibx > binding > BindingGenerator


1 /*
2 Copyright (c) 2004-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;
30
31 import java.io.FileNotFoundException;
32 import java.io.FileOutputStream;
33 import java.io.PrintStream;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.Iterator;
39
40 import org.apache.bcel.classfile.Field;
41 import org.apache.bcel.classfile.JavaClass;
42 import org.jibx.binding.classes.ClassCache;
43 import org.jibx.binding.classes.ClassFile;
44 import org.jibx.binding.classes.ClassItem;
45 import org.jibx.binding.model.BindingElement;
46 import org.jibx.binding.model.CollectionElement;
47 import org.jibx.binding.model.ContainerElementBase;
48 import org.jibx.binding.model.MappingElement;
49 import org.jibx.binding.model.NamespaceElement;
50 import org.jibx.binding.model.StructureElement;
51 import org.jibx.binding.model.StructureElementBase;
52 import org.jibx.binding.model.ValueElement;
53 import org.jibx.binding.util.ObjectStack;
54 import org.jibx.runtime.BindingDirectory;
55 import org.jibx.runtime.IBindingFactory;
56 import org.jibx.runtime.IMarshallingContext;
57 import org.jibx.runtime.JiBXException;
58
59 /**
60  * Binding generator. This loads the specified input classes and processes them
61  * to generate a default binding definition.
62  *
63  * @author Dennis M. Sosnoski
64  * @version 1.0
65  */

66  
67 public class BindingGenerator
68 {
69     /** Generator version. */
70     private static String CURRENT_VERSION = "0.2";
71     
72     /** Set of objects treated as primitives. */
73     private static HashSet s_objectPrimitiveSet = new HashSet();
74     
75     static {
76         s_objectPrimitiveSet.add("java.lang.Boolean");
77         s_objectPrimitiveSet.add("java.lang.Byte");
78         s_objectPrimitiveSet.add("java.lang.Char");
79         s_objectPrimitiveSet.add("java.lang.Double");
80         s_objectPrimitiveSet.add("java.lang.Float");
81         s_objectPrimitiveSet.add("java.lang.Integer");
82         s_objectPrimitiveSet.add("java.lang.Long");
83         s_objectPrimitiveSet.add("java.lang.Short");
84         s_objectPrimitiveSet.add("java.math.BigDecimal");
85         s_objectPrimitiveSet.add("java.math.BigInteger");
86         s_objectPrimitiveSet.add("java.sql.Date");
87         s_objectPrimitiveSet.add("java.sql.Time");
88         s_objectPrimitiveSet.add("java.sql.Timestamp");
89         s_objectPrimitiveSet.add("java.util.Date");
90     }
91     
92     /** Show verbose output flag. */
93     private boolean m_verbose;
94     
95     /** Use camel case for XML names flag. */
96     private boolean m_mixedCase;
97     
98     /** Namespace URI for elements. */
99     private String m_namespaceUri;
100     
101     /** Class names to mapped element names map. */
102     private HashMap m_mappedNames;
103     
104     /** Class names to properties list map. */
105     private HashMap m_beanNames;
106     
107     /** Class names to deserializers map for typesafe enumerations. */
108     private HashMap m_enumerationNames;
109     
110     /** Stack of structure definitions in progress (used to detect cycles). */
111     private ObjectStack m_structureStack;
112     
113     /** Class names bound as nested structures. */
114     private HashSet m_structureNames;
115     
116     /** Class names to be treated like interfaces (not mapped directly). */
117     private HashSet m_ignoreNames;
118     
119     /**
120      * Default constructor. This just initializes all options disabled.
121      */

122     public BindingGenerator() {
123         m_mappedNames = new HashMap();
124         m_structureStack = new ObjectStack();
125         m_structureNames = new HashSet();
126         m_ignoreNames = new HashSet();
127     }
128     
129     /**
130      * Constructor with settings specified.
131      *
132      * @param verbose report binding details and results
133      * @param mixed use camel case in element names
134      * @param uri namespace URI for element bindings
135      */

136     public BindingGenerator(boolean verbose, boolean mixed, String uri) {
137         this();
138         m_verbose = verbose;
139         m_mixedCase = mixed;
140         m_namespaceUri = uri;
141     }
142
143     /**
144      * Set control flag for verbose processing reports.
145      *
146      * @param verbose report verbose information in processing bindings flag
147      */

148     public void setVerbose(boolean verbose) {
149         m_verbose = verbose;
150     }
151
152     /**
153      * Set control flag for camel case element naming.
154      *
155      * @param camel use camel case element naming flag
156      */

157     public void setCamelCase(boolean camel) {
158         m_mixedCase = camel;
159     }
160     
161     /**
162      * Indent to proper depth for current item.
163      *
164      * @param pw output print stream to be indented
165      */

166     private void nestingIndent(PrintStream pw) {
167         for (int i = 0; i < m_structureStack.size(); i++) {
168             pw.print(' ');
169         }
170     }
171     
172     /**
173      * Convert class or unprefixed field name to element or attribute name.
174      *
175      * @param base class or simple field name to be converted
176      * @return element or attribute name
177      */

178     private String convertName(String base) {
179         StringBuffer name = new StringBuffer();
180         name.append(Character.toLowerCase(base.charAt(0)));
181         if (m_mixedCase) {
182             name.append(base.substring(1));
183         } else {
184             boolean ignore = true;
185             for (int i = 1; i < base.length(); i++) {
186                 char chr = base.charAt(i);
187                 if (Character.isUpperCase(chr)) {
188                     chr = Character.toLowerCase(chr);
189                     if (ignore) {
190                         int next = i + 1;
191                         ignore = next >= base.length() ||
192                             Character.isUpperCase(base.charAt(next));
193                     }
194                     if (ignore) {
195                         name.append(chr);
196                     } else {
197                         name.append('-');
198                         name.append(chr);
199                         ignore = true;
200                     }
201                 } else {
202                     name.append(chr);
203                     ignore = false;
204                 }
205             }
206         }
207         return name.toString();
208     }
209     
210     /**
211      * Generate structure element name from class name using set conversions.
212      *
213      * @param cname class name to be converted
214      * @return element name for instances of class
215      */

216     public String elementName(String cname) {
217         int split = cname.lastIndexOf('.');
218         if (split >= 0) {
219             cname = cname.substring(split+1);
220         }
221         while ((split = cname.indexOf('$')) >= 0) {
222             cname = cname.substring(0, split) + cname.substring(split+1);
223         }
224         return convertName(cname);
225     }
226     
227     /**
228      * Generate structure element name from class name using set conversions.
229      *
230      * @param fname field name to be converted
231      * @return element name for instances of class
232      */

233     private String valueName(String fname) {
234         String base = fname;
235         if (base.startsWith("m_")) {
236             base = base.substring(2);
237         } else if (base.startsWith("_")) {
238             base = base.substring(1);
239         } else if (base.startsWith("f") && base.length() > 1) {
240             if (Character.isUpperCase(base.charAt(1))) {
241                 base = base.substring(1);
242             }
243         }
244         return convertName(base);
245     }
246     
247     /**
248      * Construct the list of child binding components that define the binding
249      * structure for fields of a particular class. This binds all
250      * non-final/non-static/non-transient fields of the class, if necessary
251      * creating nested structure elements for unmapped classes referenced by the
252      * fields.
253      *
254      * @param cf class information
255      * @param contain binding structure container element
256      * @throws JiBXException on error in binding generation
257      */

258     private void defineFields(ClassFile cf, ContainerElementBase contain)
259         throws JiBXException {
260         
261         // process all non-final/non-static/non-transient fields of class
262
JavaClass clas = cf.getRawClass();
263         Field[] fields = clas.getFields();
264         for (int i = 0; i < fields.length; i++) {
265             Field field = fields[i];
266             if (!field.isFinal() && !field.isStatic() && !field.isTransient()) {
267                 
268                 // find type of handling needed for field type
269
boolean simple = false;
270                 boolean object = true;
271                 boolean attribute = true;
272                 String fname = field.getName();
273                 String tname = field.getType().toString();
274                 if (ClassItem.isPrimitive(tname)) {
275                     simple = true;
276                     object = false;
277                 } else if (s_objectPrimitiveSet.contains(tname)) {
278                     simple = true;
279                 } else if (tname.equals("byte[]")) {
280                     simple = true;
281                     attribute = false;
282                 } else if (tname.startsWith("java.")) {
283                     
284                     // check for standard library classes we can handle
285
ClassFile pcf = ClassCache.getClassFile(tname);
286                     ClassItem init = pcf.getInitializerMethod
287                         ("(Ljava/lang/String;)");
288                     if (init != null) {
289                         simple = pcf.getMethod("toString",
290                             "()Ljava/lang/String;") != null;
291                         attribute = false;
292                     }
293                     
294                 }
295                 
296                 // check type of handling to use for value
297
if (m_enumerationNames.get(tname) != null) {
298                     
299                     // define a value using deserializer method
300
String mname = (String)m_enumerationNames.get(tname);
301                     int split = mname.lastIndexOf('.');
302                     ClassFile dcf =
303                         ClassCache.getClassFile(mname.substring(0, split));
304                     ClassItem dser =
305                         dcf.getStaticMethod(mname.substring(split+1),
306                         "(Ljava/lang/String;)");
307                     if (dser == null || !tname.equals(dser.getTypeName())) {
308                         throw new JiBXException("Deserializer method not " +
309                             "found for enumeration class " + tname);
310                     }
311                     ValueElement value = new ValueElement();
312                     value.setFieldName(fname);
313                     value.setName(valueName(fname));
314                     value.setUsageName("optional");
315                     value.setStyleName("element");
316                     value.setDeserializerName(mname);
317                     contain.addChild(value);
318                     
319                 } else if (m_mappedNames.get(tname) != null) {
320                     
321                     // use mapping definition for class
322
StructureElement structure = new StructureElement();
323                     structure.setUsageName("optional");
324                     structure.setFieldName(fname);
325                     if (((String)m_mappedNames.get(tname)).length() == 0) {
326                         
327                         // add a name for reference to abstract mapping
328
structure.setName(elementName(tname));
329                     }
330                     contain.addChild(structure);
331                     if (m_verbose) {
332                         nestingIndent(System.out);
333                         System.out.println("referenced existing binding for " +
334                             tname);
335                     }
336                     
337                 } else if (simple) {
338                     
339                     // define a simple value
340
ValueElement value = new ValueElement();
341                     value.setFieldName(fname);
342                     value.setName(valueName(fname));
343                     if (object) {
344                         value.setUsageName("optional");
345                     }
346                     if (!attribute) {
347                         value.setStyleName("element");
348                     }
349                     contain.addChild(value);
350                     
351                 } else if (tname.endsWith("[]")) {
352                     
353                     // array, only supported for mapped base type
354
String bname = tname.substring(0, tname.length()-2);
355                     if (m_mappedNames.get(bname) == null) {
356                         throw new JiBXException("Base element type " + bname +
357                             " must be mapped");
358                     } else {
359                         StructureElement structure = new StructureElement();
360                         structure.setUsageName("optional");
361                         structure.setFieldName(fname);
362                         structure.setMarshallerName
363                             ("org.jibx.extras.TypedArrayMapper");
364                         structure.setUnmarshallerName
365                             ("org.jibx.extras.TypedArrayMapper");
366                         contain.addChild(structure);
367                     }
368                     
369                 } else {
370                     
371                     // no defined handling, check for collection vs. structure
372
ClassFile pcf = ClassCache.getClassFile(tname);
373                     StructureElementBase element;
374                     if (pcf.isImplements("Ljava/util/List;")) {
375                         
376                         // create a collection for list subclass
377
System.err.println("Warning: field " + fname +
378                             " requires mapped implementation of item classes");
379                         element = new CollectionElement();
380                         element.setComment(" add details of collection items " +
381                             "to complete binding definition ");
382                         element.setFieldName(fname);
383                         
384                         // specify factory method if just typed as interface
385
if ("java.util.List".equals(tname)) {
386                             element.setFactoryName
387                                 ("org.jibx.runtime.Utility.arrayListFactory");
388                         }
389                         
390                     } else if (pcf.isInterface() ||
391                         m_ignoreNames.contains(tname)) {
392                         
393                         // create mapping reference with warning for interface
394
nestingIndent(System.err);
395                         System.err.println("Warning: reference to interface " +
396                             "or abstract class " + tname +
397                             " requires mapped implementation");
398                         element = new StructureElement();
399                         element.setFieldName(fname);
400                         
401                     } else {
402                         
403                         // handle other types of structures directly
404
element = createStructure(pcf, fname);
405                     }
406                     
407                     // finish with common handling
408
element.setUsageName("optional");
409                     contain.addChild(element);
410                     
411                 }
412             }
413         }
414     }
415     
416     /**
417      * Construct the list of child binding components that define the binding
418      * structure corresponding to properties of a particular class. This binds
419      * the specified properties of the class, using get/set methods, if
420      * necessary creating nested structure elements for unmapped classes
421      * referenced by the properties.
422      *
423      * @param cf class information
424      * @param props list of properties specified for class
425      * @param internal allow private get/set methods flag
426      * @param contain binding structure container element
427      * @throws JiBXException on error in binding generation
428      */

429     private void defineProperties(ClassFile cf, ArrayList props,
430         boolean internal, ContainerElementBase contain) throws JiBXException {
431         
432         // process all properties of class
433
for (int i = 0; i < props.size(); i++) {
434             String pname = (String)props.get(i);
435             String base = Character.toUpperCase(pname.charAt(0)) +
436                 pname.substring(1);
437             String gname = "get" + base;
438             ClassItem gmeth = cf.getMethod(gname, "()");
439             if (gmeth == null) {
440                 throw new JiBXException("No method " + gname +
441                     "() found for property " + base + " in class " +
442                     cf.getName());
443             } else {
444                 String tname = gmeth.getTypeName();
445                 String sname = "set" + base;
446                 String sig = "(" + gmeth.getSignature().substring(2) + ")V";
447                 ClassItem smeth = cf.getMethod(sname, sig);
448                 if (smeth == null) {
449                     throw new JiBXException("No method " + sname + "(" + tname +
450                         ") found for property " + base + " in class " +
451                         cf.getName());
452                 } else {
453                     
454                     // find type of handling needed for field type
455
boolean simple = false;
456                     boolean object = true;
457                     boolean attribute = true;
458                     if (ClassItem.isPrimitive(tname)) {
459                         simple = true;
460                         object = false;
461                     } else if (s_objectPrimitiveSet.contains(tname)) {
462                         simple = true;
463                     } else if (tname.equals("byte[]")) {
464                         simple = true;
465                         attribute = false;
466                     } else if (tname.startsWith("java.")) {
467                         
468                         // check for standard library classes we can handle
469
ClassFile pcf = ClassCache.getClassFile(tname);
470                         if (pcf.getInitializerMethod("()") != null) {
471                             simple = pcf.getMethod("toString",
472                                 "()Ljava/lang/String;") != null;
473                             attribute = false;
474                         }
475                         
476                     }
477                     if (simple) {
478                         
479                         // define a simple value
480
ValueElement value = new ValueElement();
481                         value.setGetName(gname);
482                         value.setSetName(sname);
483                         value.setName(valueName(pname));
484                         if (object) {
485                             value.setUsageName("optional");
486                         }
487                         if (!attribute) {
488                             value.setStyleName("element");
489                         }
490                         contain.addChild(value);
491                         
492                     } else if (m_enumerationNames.get(tname) != null) {
493                         
494                         // define a value using deserializer method
495
String mname = (String)m_enumerationNames.get(tname);
496                         int split = mname.lastIndexOf('.');
497                         ClassFile dcf =
498                             ClassCache.getClassFile(mname.substring(0, split));
499                         ClassItem dser =
500                             dcf.getStaticMethod(mname.substring(split+1),
501                             "(Ljava/lang/String;)");
502                         if (dser == null || !tname.equals(dser.getTypeName())) {
503                             throw new JiBXException("Deserializer method not " +
504                                 "found for enumeration class " + tname);
505                         }
506                         ValueElement value = new ValueElement();
507                         value.setGetName(gname);
508                         value.setSetName(sname);
509                         value.setName(valueName(pname));
510                         value.setUsageName("optional");
511                         value.setStyleName("element");
512                         value.setDeserializerName(mname);
513                         contain.addChild(value);
514                         
515                     } else if (m_mappedNames.get(tname) != null) {
516                         
517                         // use mapping definition for class
518
StructureElement structure = new StructureElement();
519                         structure.setUsageName("optional");
520                         structure.setGetName(gname);
521                         structure.setSetName(sname);
522                         if (((String)m_mappedNames.get(tname)).length() == 0) {
523                             
524                             // add a name for reference to abstract mapping
525
structure.setName(elementName(tname));
526                         }
527                         contain.addChild(structure);
528                         if (m_verbose) {
529                             nestingIndent(System.out);
530                             System.out.println
531                                 ("referenced existing binding for " + tname);
532                         }
533                         
534                     } else if (tname.endsWith("[]")) {
535                         
536                         // array, only supported for mapped base type
537
String bname = tname.substring(0, tname.length()-2);
538                         if (m_mappedNames.get(bname) == null) {
539                             throw new JiBXException("Base element type " +
540                                 bname + " must be mapped");
541                         } else {
542                             StructureElement structure = new StructureElement();
543                             structure.setUsageName("optional");
544                             structure.setGetName(gname);
545                             structure.setSetName(sname);
546                             structure.setMarshallerName
547                                 ("org.jibx.extras.TypedArrayMapper");
548                             structure.setUnmarshallerName
549                                 ("org.jibx.extras.TypedArrayMapper");
550                             contain.addChild(structure);
551                         }
552                         
553                     } else {
554                         
555                         // no defined handling, check collection vs. structure
556
ClassFile pcf = ClassCache.getClassFile(tname);
557                         StructureElementBase element;
558                         if (pcf.isImplements("Ljava/util/List;")) {
559                             
560                             // create a collection for list subclass
561
System.err.println("Warning: property " + pname +
562                                 " requires mapped implementation of item " +
563                                 "classes");
564                             element = new CollectionElement();
565                             element.setComment(" add details of collection " +
566                                 "items to complete binding definition ");
567                             element.setGetName(gname);
568                             element.setSetName(sname);
569                             
570                             // specify factory method if just typed as interface
571
if ("java.util.List".equals(tname)) {
572                                 element.setFactoryName("org.jibx.runtime." +
573                                     "Utility.arrayListFactory");
574                             }
575                             
576                         } else if (pcf.isInterface() ||
577                             m_ignoreNames.contains(tname)) {
578                             
579                             // mapping reference with warning for interface
580
nestingIndent(System.err);
581                             System.err.println("Warning: reference to " +
582                                 "interface or abstract class " + tname +
583                                 " requires mapped implementation");
584                             element = new StructureElement();
585                             element.setGetName(gname);
586                             element.setSetName(sname);
587                             
588                         } else {
589                             
590                             // handle other types of structures directly
591
element = createStructure(pcf, pname);
592                         }
593                         
594                         // finish with common handling
595
element.setUsageName("optional");
596                         contain.addChild(element);
597                         
598                     }
599                 }
600             }
601         }
602     }
603     
604     /**
605      * Construct the list of child binding components that define the binding
606      * structure corresponding to a particular class. This binds all
607      * non-final/non-static/non-transient fields of the class, if necessary
608      * creating nested structure elements for unmapped classes referenced by the
609      * fields.
610      *
611      * @param clas class information
612      * @param contain binding structure container element
613      * @throws JiBXException on error in binding generation
614      */

615     private void defineStructure(ClassFile cf, ContainerElementBase contain)
616         throws JiBXException {
617         
618         // process basic fields or property list
619
Object props = m_beanNames.get(cf.getName());
620         if (props == null) {
621             defineFields(cf, contain);
622         } else {
623             defineProperties(cf, (ArrayList)props, true, contain);
624         }
625         
626         // check if superclass may have data for binding
627
ClassFile sf = cf.getSuperFile();
628         String sname = sf.getName();
629         if (!"java.lang.Object".equals(sname) &&
630             !m_ignoreNames.contains(sname)) {
631             if (m_mappedNames.get(sname) != null) {
632                 StructureElement structure = new StructureElement();
633                 structure.setMapAsName(sname);
634                 structure.setName(elementName(sname));
635                 contain.addChild(structure);
636             } else if (m_beanNames.get(sname) != null) {
637                 defineProperties(sf, (ArrayList)m_beanNames.get(sname),
638                     false, contain);
639             } else if (sf.getRawClass().getFields().length > 0) {
640                 nestingIndent(System.err);
641                 System.err.println("Warning: fields from base class " + sname +
642                     " of class " + cf.getName() + " not handled by generated " +
643                     "binding; use mapping or specify property list");
644                 contain.setComment(" missing information for base class " +
645                     sname + " ");
646             }
647         }
648     }
649     
650     /**
651      * Create the structure element for a particular class. This maps all
652      * non-final/non-static/non-transient fields of the class, if necessary
653      * creating nested structures.
654      *
655      * @param clas class information
656      * @param fname name of field supplying reference
657      * @throws JiBXException on error in binding generation
658      */

659     private StructureElement createStructure(ClassFile cf, String fname)
660         throws JiBXException {
661         JavaClass clas = cf.getRawClass();
662         if (m_verbose) {
663             nestingIndent(System.out);
664             System.out.println("creating nested structure definition for " +
665                 clas.getClassName());
666         }
667         String cname = clas.getClassName();
668         for (int i = 0; i < m_structureStack.size(); i++) {
669             if (cname.equals(m_structureStack.peek(i))) {
670                 StringBuffer buff =
671                     new StringBuffer("Error: recursive use of ");
672                 buff.append(cname);
673                 buff.append(" requires <mapping>:\n ");
674                 while (i >= 0) {
675                     buff.append(m_structureStack.peek(i--));
676                     buff.append(" -> ");
677                 }
678                 buff.append(cname);
679                 throw new JiBXException(buff.toString());
680             }
681         }
682         if (cname.startsWith("java.")) {
683             nestingIndent(System.err);
684             System.err.println("Warning: trying to create structure for " +
685                 cname);
686         } else if (m_structureNames.contains(cname)) {
687             nestingIndent(System.err);
688             System.err.println("Warning: repeated usage of class " +
689                 cname + "; consider adding to mapping list");
690         } else {
691             m_structureNames.add(cname);
692         }
693         m_structureStack.push(cname);
694         StructureElement element = new StructureElement();
695         element.setFieldName(fname);
696         element.setName(valueName(fname));
697         defineStructure(cf, element);
698         if (element.children().isEmpty()) {
699             throw new JiBXException("No content found for class " + cname);
700         }
701         m_structureStack.pop();
702         if (m_verbose) {
703             nestingIndent(System.out);
704             System.out.println("completed nested structure definition for " +
705                 clas.getClassName());
706         }
707         return element;
708     }
709
710     /**
711      * Create the mapping element for a particular class. This maps all
712      * non-final/non-static/non-transient fields of the class, if necessary
713      * creating nested structures.
714      *
715      * @param clas class information
716      * @param abstr force abstract mapping flag
717      * @throws JiBXException on error in binding generation
718      */

719     private MappingElement createMapping(ClassFile cf, boolean abstr)
720         throws JiBXException {
721         JavaClass clas = cf.getRawClass();
722         if (m_verbose) {
723             System.out.println("\nBuilding mapping definition for " +
724                 clas.getClassName());
725         }
726         MappingElement element = new MappingElement();
727         element.setAbstract(abstr || clas.isAbstract() || clas.isInterface());
728         String name = clas.getClassName();
729         element.setClassName(name);
730         if (abstr) {
731             element.setAbstract(true);
732         } else {
733             element.setName((String)m_mappedNames.get(name));
734         }
735         m_structureStack.push(name);
736         defineStructure(cf, element);
737         m_structureStack.pop();
738         return element;
739     }
740     
741     private static boolean isMappable(String cname) {
742         if ("java.lang.String".equals(cname)) {
743             return false;
744         } else if ("java.lang.Object".equals(cname)) {
745             return false;
746         } else if (ClassItem.isPrimitive(cname)) {
747             return false;
748         } else {
749             return !s_objectPrimitiveSet.contains(cname);
750         }
751     }
752
753     /**
754      * Get the set of data classes passed to or returned by a list of methods
755      * within a class. The classes returned exclude primitive types, wrappers,
756      * <code>java.lang.String</code>, and <code>java.lang.Object</code>.
757      * Exception classes thrown by the methods are also optionally accumulated.
758      *
759      * @param cname target class name
760      * @param mnames method names to be checked
761      * @param dataset set for accumulation of data classes (optional, data
762      * classes not recorded if <code>null</code>)
763      * @param exceptset set for accumulation of exception classes (optional,
764      * data classes not recorded if <code>null</code>)
765      * @throws JiBXException on error in loading class information
766      */

767     public static void findClassesUsed(String cname, ArrayList mnames,
768         HashSet dataset, HashSet exceptset) throws JiBXException {
769         ClassFile cf = ClassCache.getClassFile(cname);
770         if (cf != null) {
771             for (int i = 0; i < mnames.size(); i++) {
772                 String mname = (String)mnames.get(i);
773                 ClassItem mitem = cf.getMethod(mname, "");
774                 if (mitem == null) {
775                     System.err.println("Method " + mname +
776                         " not found in class " + cname);
777                 } else {
778                     if (dataset != null) {
779                         String type = mitem.getTypeName();
780                         if (type != null && isMappable(type)) {
781                             dataset.add(type);
782                         }
783                         String[] args = mitem.getArgumentTypes();
784                         for (int j = 0; j < args.length; j++) {
785                             type = args[j];
786                             if (isMappable(type)) {
787                                 dataset.add(args[j]);
788                             }
789                         }
790                     }
791                     if (exceptset != null) {
792                         String[] excepts = mitem.getExceptions();
793                         for (int j = 0; j < excepts.length; j++) {
794                             exceptset.add(excepts[j]);
795                         }
796                     }
797                 }
798             }
799         }
800     }
801     
802     /**
803      * Generate a set of bindings using supplied classpaths and class names.
804      *
805      * @param names list of class names to be included in binding
806      * @param abstracts set of classes to be handled with abstract mappings in
807      * binding
808      * @param customs map of customized class names to marshaller/unmarshaller
809      * class names
810      * @param beans map of class names to supplied lists of properties
811      * @param enums map of typesafe enumeration classes to deserializer methods
812      * @param ignores list of non-interface classes to be treated as interfaces
813      * (no mapping, but mapped subclasses are used at runtime)
814      * @exception JiBXException if error in generating the binding definition
815      */

816     public BindingElement generate(ArrayList names, HashSet abstracts,
817         HashMap customs, HashMap beans, HashMap enums, ArrayList ignores)
818         throws JiBXException {
819         
820         // print current version information
821
System.out.println("Running binding generator version " +
822             CURRENT_VERSION);
823         
824         // add all classes with mappings to tracking map
825
m_mappedNames.clear();
826         for (int i = 0; i < names.size(); i++) {
827             
828             // make sure class can potentially be handled automatically
829
boolean drop = false;
830             String name = (String)names.get(i);
831             ClassFile cf = ClassCache.getClassFile(name);
832             if (cf.isImplements("Ljava/util/List;") ||
833                 cf.isImplements("Ljava/util/Map;")) {
834                 System.err.println("Warning: referenced class " + name +
835                     " is a collection class that cannot be mapped " +
836                     "automatically; dropped from mapped list in binding");
837                 drop = true;
838             } else if (cf.isInterface()) {
839                 System.err.println("Warning: interface " + name +
840                     " is being handled as abstract mapping");
841                 abstracts.add(name);
842                 drop = true;
843             } else if (cf.isAbstract()) {
844                 System.err.println("Warning: mapping abstract class " + name +
845                     "; make sure actual subclasses are mapped as extending " +
846                     "this abstract mapping");
847                 abstracts.add(name);
848                 drop = true;
849             }
850             if (drop) {
851                 names.remove(i--);
852             } else {
853                 m_mappedNames.put(name, elementName(name));
854             }
855         }
856         for (Iterator iter = abstracts.iterator(); iter.hasNext();) {
857             m_mappedNames.put((String)iter.next(), "");
858         }
859         for (Iterator iter = customs.keySet().iterator(); iter.hasNext();) {
860             String name = (String)iter.next();
861             m_mappedNames.put(name, elementName(name));
862         }
863         
864         // create set for ignores
865
m_ignoreNames.clear();
866         m_ignoreNames.addAll(ignores);
867         
868         // create binding with optional namespace
869
BindingElement binding = new BindingElement();
870         binding.setStyleName("attribute");
871         if (m_namespaceUri != null) {
872             NamespaceElement namespace = new NamespaceElement();
873             namespace.setComment(" namespace for all elements of binding ");
874             namespace.setDefaultName("elements");
875             namespace.setUri(m_namespaceUri);
876             binding.addTopChild(namespace);
877         }
878         
879         // add mapping for each specified class
880
m_structureStack.clear();
881         m_structureNames.clear();
882         m_beanNames = beans;
883         m_enumerationNames = enums;
884         for (int i = 0; i < names.size(); i++) {
885             String cname = (String)names.get(i);
886             if (!abstracts.contains(cname)) {
887                 ClassFile cf = ClassCache.getClassFile(cname);
888                 MappingElement mapping = createMapping(cf, false);
889                 mapping.setComment(" generated mapping for class " + cname);
890                 binding.addTopChild(mapping);
891             }
892         }
893         for (Iterator iter = abstracts.iterator(); iter.hasNext();) {
894             String cname = (String)iter.next();
895             ClassFile cf = ClassCache.getClassFile(cname);
896             MappingElement mapping = createMapping(cf, true);
897             mapping.setComment(" generate abstract mapping for class " + cname);
898             binding.addTopChild(mapping);
899         }
900         
901         // finish with custom mapping definitions
902
for (Iterator iter = customs.keySet().iterator(); iter.hasNext();) {
903             String cname = (String)iter.next();
904             String mname = (String)customs.get(cname);
905             MappingElement mapping = new MappingElement();
906             mapping.setComment(" specified mapping for class " + cname);
907             mapping.setClassName(cname);
908             mapping.setName((String)m_mappedNames.get(cname));
909             mapping.setMarshallerName(mname);
910             mapping.setUnmarshallerName(mname);
911             binding.addTopChild(mapping);
912         }
913         
914         // list classes handled if verbose
915
if (m_verbose) {
916             for (int i = 0; i < names.size(); i++) {
917                 m_structureNames.add(names.get(i));
918             }
919             m_structureNames.addAll(customs.keySet());
920             String[] classes = (String[])m_structureNames.
921                 toArray(new String[m_structureNames.size()]);
922             Arrays.sort(classes);
923             System.out.println("\nClasses included in binding:");
924             for (int i = 0; i < classes.length; i++) {
925                 System.out.println(" " + classes[i]);
926             }
927         }
928         return binding;
929     }
930     
931     /**
932      * Main method for running compiler as application.
933      *
934      * @param args command line arguments
935      */

936     public static void main(String[] args) {
937         if (args.length > 0) {
938             try {
939                 
940                 // check for various flags set
941
boolean mixed = false;
942                 boolean verbose = false;
943                 String uri = null;
944                 String fname = "binding.xml";
945                 ArrayList paths = new ArrayList();
946                 HashMap enums = new HashMap();
947                 HashSet abstracts = new HashSet();
948                 ArrayList ignores = new ArrayList();
949                 HashMap customs = new HashMap();
950                 HashMap beans = new HashMap();
951                 int offset = 0;
952                 for (; offset < args.length; offset++) {
953                     String arg = args[offset];
954                     if ("-v".equalsIgnoreCase(arg)) {
955                         verbose = true;
956                     } else if ("-b".equalsIgnoreCase(arg)) {
957                         String plist = args[++offset];
958                         int split = plist.indexOf("=");
959                         String bname = plist.substring(0, split);
960                         ArrayList props = new ArrayList();
961                         int base = ++split;
962                         while ((split = plist.indexOf(',', split)) >= 0) {
963                             props.add(plist.substring(base, split));
964                             base = ++split;
965                         }
966                         props.add(plist.substring(base));
967                         beans.put(bname, props);
968                     } else if ("-c".equalsIgnoreCase(arg)) {
969                         String custom = args[++offset];
970                         int split = custom.indexOf('=');
971                         if (split > 0) {
972                             customs.put(custom.substring(0, split),
973                                 custom.substring(split+1));
974                         } else {
975                             System.err.println
976                                 ("Unable to interpret customization " + custom);
977                         }
978                     } else if ("-e".equalsIgnoreCase(arg)) {
979                         String cname;
980                         String mname;
981                         String enum = args[++offset];
982                         int split = enum.indexOf('=');
983                         if (split > 0) {
984                             cname = enum.substring(0, split);
985                             mname = enum.substring(split+1);
986                             if (mname.indexOf('.') < 0) {
987                                 mname = cname + '.' + mname;
988                             }
989                         } else {
990                             cname = enum;
991                             split = cname.lastIndexOf('.');
992                             if (split >= 0) {
993                                 mname = cname + '.' + "get" +
994                                     cname.substring(split+1);
995                             } else {
996                                 mname = cname + '.' + "get" + cname;
997                             }
998                         }
999                         enums.put(cname, mname);
1000                    } else if ("-i".equalsIgnoreCase(arg)) {
1001                        ignores.add(args[++offset]);
1002                    } else if ("-m".equalsIgnoreCase(arg)) {
1003                        mixed = true;
1004                    } else if ("-f".equalsIgnoreCase(arg)) {
1005                        fname = args[++offset];
1006                    } else if ("-n".equalsIgnoreCase(arg)) {
1007                        uri = args[++offset];
1008                    } else if ("-p".equalsIgnoreCase(arg)) {
1009                        paths.add(args[++offset]);
1010                    } else if ("-a".equalsIgnoreCase(arg)) {
1011                        abstracts.add(args[++offset]);
1012                    } else {
1013                        break;
1014                    }
1015                }
1016                
1017                // set up path and binding lists
1018
String[] vmpaths = Utility.getClassPaths();
1019                for (int i = 0; i < vmpaths.length; i++) {
1020                    paths.add(vmpaths[i]);
1021                }
1022                ArrayList names = new ArrayList();
1023                for (int i = offset; i < args.length; i++) {
1024                    if (!abstracts.contains(args[i])) {
1025                        names.add(args[i]);
1026                    }
1027                }
1028                
1029                // report on the configuration
1030
if (verbose) {
1031                    System.out.println("Using paths:");
1032                    for (int i = 0; i < paths.size(); i++) {
1033                        System.out.println(" " + paths.get(i));
1034                    }
1035                    System.out.println("Using class names:");
1036                    for (int i = 0; i < names.size(); i++) {
1037                        System.out.println(" " + names.get(i));
1038                    }
1039                    if (abstracts.size() > 0) {
1040                        System.out.println("Using abstract class names:");
1041                        for (Iterator iter = abstracts.iterator();
1042                            iter.hasNext();) {
1043                            System.out.println(" " + iter.next());
1044                        }
1045                    }
1046                    if (customs.size() > 0) {
1047                        System.out.println
1048                            ("Using custom marshaller/unmarshallers:");
1049                        Iterator iter = customs.keySet().iterator();
1050                        while (iter.hasNext()) {
1051                            String key = (String)iter.next();
1052                            System.out.println(" " + customs.get(key) +
1053                                " for " + key);
1054                        }
1055                    }
1056                    if (beans.size() > 0) {
1057                        System.out.println("Using bean property lists:");
1058                        Iterator iter = beans.keySet().iterator();
1059                        while (iter.hasNext()) {
1060                            String key = (String)iter.next();
1061                            System.out.print(" " + key + " with properties:");
1062                            ArrayList props = (ArrayList)beans.get(key);
1063                            for (int i = 0; i < props.size(); i++) {
1064                                System.out.print(i > 0 ? ", " : " ");
1065                                System.out.print(props.get(i));
1066                            }
1067                        }
1068                    }
1069                    if (enums.size() > 0) {
1070                        System.out.println("Using enumeration classes:");
1071                        Iterator iter = enums.keySet().iterator();
1072                        while (iter.hasNext()) {
1073                            String key = (String)iter.next();
1074                            System.out.println(" " + key +
1075                                " with deserializer " + enums.get(key));
1076                        }
1077                    }
1078                }
1079                
1080                // set paths to be used for loading referenced classes
1081
String[] parray =
1082                    (String[])paths.toArray(new String[paths.size()]);
1083                ClassCache.setPaths(parray);
1084                ClassFile.setPaths(parray);
1085                
1086                // generate the binding
1087
BindingGenerator generate =
1088                    new BindingGenerator(verbose, mixed, uri);
1089                BindingElement binding = generate.generate(names, abstracts,
1090                    customs, beans, enums, ignores);
1091                
1092                // marshal binding out to file
1093
IBindingFactory bfact =
1094                    BindingDirectory.getFactory(BindingElement.class);
1095                IMarshallingContext mctx = bfact.createMarshallingContext();
1096                mctx.setIndent(2);
1097                mctx.marshalDocument(binding, "UTF-8", null,
1098                    new FileOutputStream(fname));
1099                
1100            } catch (JiBXException ex) {
1101                ex.printStackTrace(System.out);
1102                System.exit(1);
1103            } catch (FileNotFoundException e) {
1104                e.printStackTrace(System.out);
1105                System.exit(2);
1106            }
1107            
1108        } else {
1109            System.out.println
1110                ("\nUsage: java org.jibx.binding.BindingGenerator [options] " +
1111                "class1 class2 ...\nwhere options are:\n -a abstract class\n" +
1112                " -b bean property class,\n -c custom mapped class,\n" +
1113                " -e enumeration class,\n -f binding file name,\n" +
1114                " -i ignore class,\n -m use mixed case XML names (instead " +
1115                "of hyphenated),\n -n namespace URI,\n -p class " +
1116                "loading path component,\n -v turns on verbose output\n" +
1117                "The class# files are different classes to be included in " +
1118                "binding (references from\nthese classes will also be " +
1119                "included).\n");
1120            System.exit(1);
1121        }
1122    }
1123}
Popular Tags