KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jibx > binding > def > DirectObject


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.def;
30
31 import org.apache.bcel.Constants;
32 import org.apache.bcel.generic.Type;
33
34 import org.jibx.binding.classes.*;
35 import org.jibx.runtime.JiBXException;
36
37 /**
38  * Linkage to object with supplied marshaller and unmarshaller. This provides
39  * methods used to generate code for calling the supplied classes.
40  *
41  * @author Dennis M. Sosnoski
42  * @version 1.0
43  */

44
45 public class DirectObject implements IComponent
46 {
47     //
48
// Constants and such related to code generation.
49

50     private static final String JavaDoc GETUNMARSHALLER_METHOD =
51         "org.jibx.runtime.IUnmarshallingContext.getUnmarshaller";
52     private static final String JavaDoc GETUNMARSHALLER_SIGNATURE =
53         "(I)Lorg/jibx/runtime/IUnmarshaller;";
54     private static final String JavaDoc GETMARSHALLER_METHOD =
55         "org.jibx.runtime.IMarshallingContext.getMarshaller";
56     private static final String JavaDoc GETMARSHALLER_SIGNATURE =
57         "(ILjava/lang/String;)Lorg/jibx/runtime/IMarshaller;";
58     private static final String JavaDoc MARSHALLER_MARSHAL_METHOD =
59         "org.jibx.runtime.IMarshaller.marshal";
60     private static final String JavaDoc MARSHALLER_MARSHAL_SIGNATURE =
61         "(Ljava/lang/Object;Lorg/jibx/runtime/IMarshallingContext;)V";
62     private static final String JavaDoc UNMARSHALLER_TESTPRESENT_METHOD =
63         "org.jibx.runtime.IUnmarshaller.isPresent";
64     private static final String JavaDoc UNMARSHALLER_TESTPRESENT_SIGNATURE =
65         "(Lorg/jibx/runtime/IUnmarshallingContext;)Z";
66     private static final String JavaDoc UNMARSHALLER_UNMARSHAL_METHOD =
67         "org.jibx.runtime.IUnmarshaller.unmarshal";
68     private static final String JavaDoc UNMARSHALLER_UNMARSHAL_SIGNATURE =
69         "(Ljava/lang/Object;Lorg/jibx/runtime/IUnmarshallingContext;)" +
70         "Ljava/lang/Object;";
71     private static final String JavaDoc ABSTRACTMARSHALLER_INTERFACE =
72         "org.jibx.runtime.IAbstractMarshaller";
73     private static final String JavaDoc ABSTRACTMARSHAL_METHOD =
74         "org.jibx.runtime.IAbstractMarshaller.baseMarshal";
75     private static final String JavaDoc ABSTRACTMARSHAL_SIGNATURE =
76         MARSHALLER_MARSHAL_SIGNATURE;
77     private static final String JavaDoc ALIASABLE_INTERFACETYPE =
78         "Lorg/jibx/runtime/IAliasable;";
79     private static final String JavaDoc ANY_INIT_SIG = "()V";
80     private static final String JavaDoc ANY_INITCLASS_SIG = "(Ljava/lang/String;)V";
81     private static final String JavaDoc MARSHALUNMARSHAL_INIT_SIG =
82         "(Ljava/lang/String;ILjava/lang/String;)V";
83     private static final String JavaDoc MARSHALONLY_INIT_SIG = "(ILjava/lang/String;)V";
84     private static final String JavaDoc UNMARSHALONLY_INIT_SIG =
85         "(Ljava/lang/String;Ljava/lang/String;)V";
86     private static final String JavaDoc MARSHALUNMARSHAL_INITCLASS_SIG =
87         "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V";
88     private static final String JavaDoc MARSHALONLY_INITCLASS_SIG =
89         "(ILjava/lang/String;Ljava/lang/String;)V";
90     private static final String JavaDoc UNMARSHALONLY_INITCLASS_SIG =
91         "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V";
92
93     //
94
// Actual instance data.
95

96     /** Containing binding definition structure. */
97     private final IContainer m_parent;
98     
99     /** Definition context for resolving names. */
100     private final DefinitionContext m_defContext;
101
102     /** Abstract mapping flag. If this is set the marshalling code will call the
103       special interface method used to verify the type of a passed object and
104       marshal it with the proper handling. */

105     private final boolean m_isAbstract;
106     
107     /** Element name information (<code>null</code> if no bound element). */
108     private final NameDefinition m_name;
109     
110     /** Flag for marshaller/unmarshaller slot defined. This is done during code
111      generation, rather than during linking, so that all mapped bindings can be
112      defined with lower index numbers than for marshallers/unmarshallers used
113      only in a specific context. */

114     private boolean m_isSlotSet;
115     
116     /** Marshaller/unmarshaller slot number. */
117     private int m_mumSlot;
118     
119     /** Class handled by this binding. */
120     private final ClassFile m_targetClass;
121
122     /** Marshaller base class. */
123     private final ClassFile m_marshallerBase;
124
125     /** Unmarshaller base class. */
126     private final ClassFile m_unmarshallerBase;
127
128     /** Marshaller class (lazy create on first use if name supplied). */
129     private ClassFile m_marshaller;
130
131     /** Unmarshaller class (lazy create on first use if name supplied). */
132     private ClassFile m_unmarshaller;
133
134     /**
135      * Constructor.
136      *
137      * @param parent containing binding definition structure
138      * @param target class handled by this binding
139      * @param abs abstract mapping flag
140      * @param mcf marshaller class information (<code>null</code> if input only
141      * binding)
142      * @param ucf unmarshaller class information (<code>null</code> if output
143      * only binding)
144      * @param slot marshaller/unmarshaller slot number (<code>-1</code> if to be
145      * defined later)
146      * @param name element name information (<code>null</code> if no element
147      * name)
148      * @throws JiBXException if configuration error
149      */

150
151     public DirectObject(IContainer parent, DefinitionContext defc,
152         ClassFile target, boolean abs, ClassFile mcf, ClassFile ucf, int slot,
153         NameDefinition name) throws JiBXException {
154         
155         // initialize the basic information
156
m_parent = parent;
157         m_defContext = (defc == null) ? m_parent.getDefinitionContext() : defc;
158         m_isAbstract = abs;
159         m_targetClass = target;
160         m_marshallerBase = mcf;
161         m_unmarshallerBase = ucf;
162         m_name = name;
163         m_mumSlot = slot;
164         m_isSlotSet = slot >= 0;
165         
166         // check for marshaller and/or unmarshaller configuration valid
167
if (name == null) {
168             
169             // no name, make sure there's an appropriate constructor
170
if (mcf != null) {
171                 if (mcf.getInitializerMethod(ANY_INIT_SIG) != null) {
172                     m_marshaller = mcf;
173                 } else if (mcf.getInitializerMethod
174                     (ANY_INITCLASS_SIG) == null) {
175                     throw new JiBXException("Marshaller class " +
176                         mcf.getName() + " requires name to be set");
177                 }
178             }
179             if (ucf != null) {
180                 if (ucf.getInitializerMethod(ANY_INIT_SIG) != null) {
181                     m_unmarshaller = ucf;
182                 } else if (ucf.getInitializerMethod
183                     (ANY_INITCLASS_SIG) == null) {
184                     throw new JiBXException("Unmarshaller class " +
185                         ucf.getName() + " requires name to be set");
186                 }
187             }
188         }
189         if (name != null) {
190             
191             // name supplied, make sure both support it
192
if (mcf != null && !mcf.isImplements(ALIASABLE_INTERFACETYPE)) {
193                 throw new JiBXException("Marshaller class " +
194                     mcf.getName() + " does not allow name to be set");
195             }
196             if (ucf != null && !ucf.isImplements(ALIASABLE_INTERFACETYPE)) {
197                 throw new JiBXException("Unmarshaller class " +
198                     ucf.getName() + " does not allow name to be set");
199             }
200         }
201     }
202
203     /**
204      * Get marshaller/unmarshaller index. This is the index into the tables of
205      * marshaller and unmarshaller instances maintained by the respective
206      * contexts. On the first time call this gets the index number from the
207      * binding definition, then the index number is reused on subsequent calls.
208      *
209      * @return slot number for binding
210      * @throws JiBXException on configuration error
211      */

212
213     private int getSlot() throws JiBXException {
214         if (!m_isSlotSet) {
215             
216             // first set slot in case called recursively
217
BindingDefinition bdef = m_parent.getBindingRoot();
218             m_mumSlot = bdef.getMarshallerUnmarshallerIndex
219                 (m_targetClass.getName());
220             m_isSlotSet = true;
221             
222             // now generate the classes (if needed) and set the names
223
String JavaDoc mclas = null;
224             String JavaDoc uclas = null;
225             if (bdef.isOutput()) {
226                 if (m_marshaller == null) {
227                     createSubclass(true);
228                 }
229                 mclas = m_marshaller.getName();
230             }
231             if (bdef.isInput()) {
232                 if (m_unmarshaller == null) {
233                     createSubclass(false);
234                 }
235                 uclas = m_unmarshaller.getName();
236             }
237             bdef.setMarshallerUnmarshallerClasses(m_mumSlot, mclas, uclas);
238         }
239         return m_mumSlot;
240     }
241
242     /**
243      * Load marshaller/unmarshaller index. This is assigned by the binding
244      * definition and used thereafter.
245      *
246      * @param mb method builder
247      */

248
249     private void genLoadSlot(ContextMethodBuilder mb) throws JiBXException {
250         mb.appendLoadConstant(getSlot());
251     }
252
253     /**
254      * Create aliased subclass for marshaller or unmarshaller with element name
255      * defined by binding. If the same aliasable superclass is defined for use
256      * as both a marshaller and an unmarshaller a single subclass is generated
257      * to handle both uses.
258      *
259      * @param out <code>true</code> if alias needed for marshalling,
260      * <code>false</code> if for unmarshalling
261      * @throws JiBXException on configuration error
262      */

263
264     private void createSubclass(boolean out) throws JiBXException {
265             
266         // find initializer call to be used
267
ClassItem init = null;
268         boolean dual = false;
269         boolean classed = true;
270         boolean named = false;
271         ClassFile base = out ? m_marshallerBase : m_unmarshallerBase;
272         
273         // check for name supplied
274
if (m_name == null) {
275             init = base.getInitializerMethod(ANY_INITCLASS_SIG);
276             classed = init != null;
277         }
278         if (init == null) {
279             named = true;
280             if (m_unmarshallerBase == m_marshallerBase) {
281             
282                 // single class, look first for signature with class name
283
init = base.getInitializerMethod
284                     (MARSHALUNMARSHAL_INITCLASS_SIG);
285                 if (init == null) {
286                     classed = false;
287                     init = base.getInitializerMethod(MARSHALUNMARSHAL_INIT_SIG);
288                 }
289                 dual = true;
290             
291             } else {
292             
293                 // using only one function, first check one way with class name
294
String JavaDoc sig = out ?
295                     MARSHALONLY_INITCLASS_SIG : UNMARSHALONLY_INITCLASS_SIG;
296                 init = base.getInitializerMethod(sig);
297                 if (init == null) {
298                 
299                     // now check dual function with class name
300
sig = MARSHALUNMARSHAL_INITCLASS_SIG;
301                     init = base.getInitializerMethod(sig);
302                     dual = true;
303                     if (init == null) {
304                     
305                         // now try alternatives without class name included
306
classed = false;
307                         sig = out ? MARSHALONLY_INIT_SIG :
308                             UNMARSHALONLY_INIT_SIG;
309                         init = base.getInitializerMethod(sig);
310                         dual = false;
311                         if (init == null) {
312                             sig = MARSHALUNMARSHAL_INIT_SIG;
313                             init = base.getInitializerMethod(sig);
314                             dual = true;
315                         }
316                     
317                     }
318                 }
319             }
320         }
321         
322         // make sure the initializer is defined and public
323
if (init == null || ((init.getAccessFlags() &
324             Constants.ACC_PUBLIC) == 0)) {
325             throw new JiBXException("No usable constructor for " +
326                 "marshaller or unmarshaller based on " + base.getName());
327         }
328         
329         // get package and target name from bound class
330
String JavaDoc tname = base.getName();
331         int split = tname.lastIndexOf('.');
332         if (split >= 0) {
333             tname = tname.substring(split+1);
334         }
335         
336         // create the helper class
337
BindingDefinition def = m_parent.getBindingRoot();
338         String JavaDoc name = def.getDefaultPackage() + '.' + def.getPrefix() + tname +
339             '_' + getSlot();
340         String JavaDoc[] intfs = def.isInput() ? (def.isOutput() ?
341             MappingDefinition.BOTH_INTERFACES :
342             MappingDefinition.UNMARSHALLER_INTERFACES) :
343             MappingDefinition.MARSHALLER_INTERFACES;
344         ClassFile cf = new ClassFile(name, def.getDefaultRoot(),
345             base, Constants.ACC_PUBLIC, intfs);
346         
347         // add the public constructor method
348
ExceptionMethodBuilder mb = new ExceptionMethodBuilder("<init>",
349             Type.VOID, new Type[0], cf, Constants.ACC_PUBLIC);
350         
351         // call the superclass constructor
352
mb.appendLoadLocal(0);
353         if (m_name == null) {
354             if (named) {
355                 if (dual) {
356                     mb.appendACONST_NULL();
357                     mb.appendICONST_0();
358                     mb.appendACONST_NULL();
359                 } else if (out) {
360                     mb.appendICONST_0();
361                     mb.appendACONST_NULL();
362                 } else {
363                     mb.appendACONST_NULL();
364                     mb.appendACONST_NULL();
365                 }
366             }
367         } else {
368             if (dual) {
369                 m_name.genPushUri(mb);
370                 m_name.genPushIndexPair(mb);
371             } else if (out) {
372                 m_name.genPushIndexPair(mb);
373             } else {
374                 m_name.genPushUriPair(mb);
375             }
376         }
377         if (classed) {
378             mb.appendLoadConstant(m_targetClass.getName());
379         }
380         mb.appendCallInit(base.getName(), init.getSignature());
381             
382         // finish with return
383
mb.appendReturn();
384         mb.codeComplete(false);
385         
386         // add method and class
387
mb.addMethod();
388         cf = MungedClass.getUniqueSupportClass(cf);
389         
390         // save as appropriate type(s)
391
if (dual) {
392             m_marshaller = m_unmarshaller = cf;
393         } else if (out) {
394             m_marshaller = cf;
395         } else {
396             m_unmarshaller = cf;
397         }
398     }
399
400     /**
401      * Generate presence test code for this mapping. The generated code finds
402      * the unmarshaller and calls the test method, leaving the result on the
403      * stack.
404      *
405      * @param mb method builder
406      * @throws JiBXException if error in generating code
407      */

408
409     public void genTestPresent(ContextMethodBuilder mb) throws JiBXException {
410         
411         // start with call to unmarshalling context method to get the
412
// unmarshaller instance
413
mb.loadContext();
414         genLoadSlot(mb);
415         mb.appendCallInterface(GETUNMARSHALLER_METHOD,
416             GETUNMARSHALLER_SIGNATURE);
417         
418         // call the actual unmarshaller test method with context as argument
419
mb.loadContext();
420         mb.appendCallInterface(UNMARSHALLER_TESTPRESENT_METHOD,
421             UNMARSHALLER_TESTPRESENT_SIGNATURE);
422     }
423
424     /**
425      * Generate unmarshalling code for this mapping. The generated code finds
426      * and calls the unmarshaller with the object to be unmarshaller (which
427      * needs to be loaded on the stack by the code prior to this call, but may
428      * be <code>null</code>). The unmarshalled object (or <code>null</code> in
429      * the case of a missing optional item) is left on the stack after this
430      * call. The calling method generally needs to cast this object reference to
431      * the appropriate type before using it.
432      *
433      * @param mb method builder
434      * @throws JiBXException if error in generating code
435      */

436
437     public void genUnmarshal(ContextMethodBuilder mb) throws JiBXException {
438         
439         // start with call to unmarshalling context method to get the
440
// unmarshaller instance
441
mb.loadContext();
442         genLoadSlot(mb);
443         mb.appendCallInterface(GETUNMARSHALLER_METHOD,
444             GETUNMARSHALLER_SIGNATURE);
445         
446         // call the actual unmarshaller with object and context as arguments
447
mb.appendSWAP();
448         mb.loadContext();
449         mb.appendCallInterface(UNMARSHALLER_UNMARSHAL_METHOD,
450             UNMARSHALLER_UNMARSHAL_SIGNATURE);
451     }
452
453     /**
454      * Generate marshalling code for this mapping. The generated code finds
455      * and calls the marshaller, passing the object to be marshalled (which
456      * should have been loaded to the stack by the prior generated code)..
457      *
458      * @param mb method builder
459      * @throws JiBXException if error in configuration
460      */

461
462     public void genMarshal(ContextMethodBuilder mb) throws JiBXException {
463         
464         // start with call to marshalling context method to get the marshaller
465
// instance
466
mb.loadContext();
467         genLoadSlot(mb);
468         mb.appendLoadConstant(m_targetClass.getName());
469         mb.appendCallInterface(GETMARSHALLER_METHOD, GETMARSHALLER_SIGNATURE);
470         
471         // check for an abstract mapping being used
472
if (m_isAbstract) {
473             
474             // make sure returned marshaller implements required interface
475
mb.appendCreateCast(ABSTRACTMARSHALLER_INTERFACE);
476         
477             // swap to reorder object and marshaller
478
mb.appendSWAP();
479         
480             // call indirect marshaller for abstract base class with the object
481
// itself and context as arguments
482
mb.loadContext();
483             mb.appendCallInterface(ABSTRACTMARSHAL_METHOD,
484                 ABSTRACTMARSHAL_SIGNATURE);
485                 
486         } else {
487         
488             // swap to reorder object and marshaller
489
mb.appendSWAP();
490         
491             // call the direct marshaller with the object itself and context as
492
// arguments
493
mb.loadContext();
494             mb.appendCallInterface(MARSHALLER_MARSHAL_METHOD,
495                 MARSHALLER_MARSHAL_SIGNATURE);
496         }
497     }
498
499     /**
500      * Get target class for mapping.
501      *
502      * @return target class information
503      */

504
505     public ClassFile getTargetClass() {
506         return m_targetClass;
507     }
508
509     /**
510      * Get mapped element name.
511      *
512      * @return mapped element name information (may be <code>null</code> if no
513      * element name defined for mapping)
514      */

515
516     public NameDefinition getName() {
517         return m_name;
518     }
519
520     /**
521      * Get marshaller class used for mapping. If a name has been supplied the
522      * actual marshaller class is created by extending the base class the first
523      * time this method is called.
524      *
525      * @return marshaller class information
526      * @throws JiBXException if error in transformation
527      */

528
529     public ClassFile getMarshaller() throws JiBXException {
530         if (m_marshaller == null && m_marshallerBase != null) {
531             createSubclass(true);
532         }
533         return m_marshaller;
534     }
535
536     /**
537      * Get unmarshaller class used for mapping. If a name has been supplied the
538      * actual unmarshaller class is created by extending the base class the
539      * first time this method is called.
540      *
541      * @return unmarshaller class information
542      * @throws JiBXException if error in transformation
543      */

544
545     public ClassFile getUnmarshaller() throws JiBXException {
546         if (m_unmarshaller == null && m_unmarshallerBase != null) {
547             createSubclass(false);
548         }
549         return m_unmarshaller;
550     }
551     
552     //
553
// IComponent interface method definitions
554

555     public boolean isOptional() {
556         return false;
557     }
558
559     public boolean hasAttribute() {
560         return false;
561     }
562
563     public void genAttrPresentTest(ContextMethodBuilder mb) {
564         throw new IllegalStateException JavaDoc
565             ("Internal error - no attributes defined");
566     }
567
568     public void genAttributeUnmarshal(ContextMethodBuilder mb) {
569         throw new IllegalStateException JavaDoc
570             ("Internal error - no attributes defined");
571     }
572
573     public void genAttributeMarshal(ContextMethodBuilder mb) {
574         throw new IllegalStateException JavaDoc
575             ("Internal error - no attributes defined");
576     }
577
578     public boolean hasContent() {
579         return true;
580     }
581
582     public void genContentPresentTest(ContextMethodBuilder mb)
583         throws JiBXException {
584         genTestPresent(mb);
585     }
586
587     public void genContentUnmarshal(ContextMethodBuilder mb)
588         throws JiBXException {
589         genUnmarshal(mb);
590     }
591
592     public void genContentMarshal(ContextMethodBuilder mb)
593         throws JiBXException {
594         genMarshal(mb);
595     }
596     
597     public void genNewInstance(ContextMethodBuilder mb) {
598         throw new IllegalStateException JavaDoc
599             ("Internal error - no instance creation");
600     }
601
602     public String JavaDoc getType() {
603         return m_targetClass.getFile().getName();
604     }
605
606     public boolean hasId() {
607         return false;
608     }
609
610     public void genLoadId(ContextMethodBuilder mb) {
611         throw new IllegalStateException JavaDoc("Internal error - no ID allowed");
612     }
613
614     public boolean checkContentSequence(boolean text) {
615         return true;
616     }
617
618     public void setLinkages() throws JiBXException {
619         if (m_name != null) {
620             m_name.fixNamespace(m_defContext);
621         }
622     }
623     
624     // DEBUG
625
public void print(int depth) {
626         BindingDefinition.indent(depth);
627         System.out.println("direct marshaller/unmarshaller reference" );
628     }
629 }
630
Popular Tags