001: /*
002: Copyright (c) 2003-2005, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.binding.def;
030:
031: import org.apache.bcel.Constants;
032: import org.apache.bcel.generic.Type;
033:
034: import org.jibx.binding.classes.*;
035: import org.jibx.runtime.JiBXException;
036:
037: /**
038: * Linkage to object with supplied marshaller and unmarshaller. This provides
039: * methods used to generate code for calling the supplied classes.
040: *
041: * @author Dennis M. Sosnoski
042: * @version 1.0
043: */
044: public class DirectObject implements IComponent {
045: //
046: // Constants and such related to code generation.
047:
048: private static final String GETUNMARSHALLER_METHOD = "org.jibx.runtime.IUnmarshallingContext.getUnmarshaller";
049: private static final String GETUNMARSHALLER_SIGNATURE = "(I)Lorg/jibx/runtime/IUnmarshaller;";
050: private static final String GETMARSHALLER_METHOD = "org.jibx.runtime.IMarshallingContext.getMarshaller";
051: private static final String GETMARSHALLER_SIGNATURE = "(ILjava/lang/String;)Lorg/jibx/runtime/IMarshaller;";
052: private static final String MARSHALLER_MARSHAL_METHOD = "org.jibx.runtime.IMarshaller.marshal";
053: private static final String MARSHALLER_MARSHAL_SIGNATURE = "(Ljava/lang/Object;Lorg/jibx/runtime/IMarshallingContext;)V";
054: private static final String UNMARSHALLER_TESTPRESENT_METHOD = "org.jibx.runtime.IUnmarshaller.isPresent";
055: private static final String UNMARSHALLER_TESTPRESENT_SIGNATURE = "(Lorg/jibx/runtime/IUnmarshallingContext;)Z";
056: private static final String UNMARSHALLER_UNMARSHAL_METHOD = "org.jibx.runtime.IUnmarshaller.unmarshal";
057: private static final String UNMARSHALLER_UNMARSHAL_SIGNATURE = "(Ljava/lang/Object;Lorg/jibx/runtime/IUnmarshallingContext;)"
058: + "Ljava/lang/Object;";
059: private static final String ABSTRACTMARSHALLER_INTERFACE = "org.jibx.runtime.IAbstractMarshaller";
060: private static final String ABSTRACTMARSHAL_METHOD = "org.jibx.runtime.IAbstractMarshaller.baseMarshal";
061: private static final String ABSTRACTMARSHAL_SIGNATURE = MARSHALLER_MARSHAL_SIGNATURE;
062: private static final String ALIASABLE_INTERFACETYPE = "Lorg/jibx/runtime/IAliasable;";
063: private static final String ANY_INIT_SIG = "()V";
064: private static final String ANY_INITCLASS_SIG = "(Ljava/lang/String;)V";
065: private static final String MARSHALUNMARSHAL_INIT_SIG = "(Ljava/lang/String;ILjava/lang/String;)V";
066: private static final String MARSHALONLY_INIT_SIG = "(ILjava/lang/String;)V";
067: private static final String UNMARSHALONLY_INIT_SIG = "(Ljava/lang/String;Ljava/lang/String;)V";
068: private static final String MARSHALUNMARSHAL_INITCLASS_SIG = "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V";
069: private static final String MARSHALONLY_INITCLASS_SIG = "(ILjava/lang/String;Ljava/lang/String;)V";
070: private static final String UNMARSHALONLY_INITCLASS_SIG = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V";
071:
072: //
073: // Actual instance data.
074:
075: /** Containing binding definition structure. */
076: private final IContainer m_parent;
077:
078: /** Definition context for resolving names. */
079: private final DefinitionContext m_defContext;
080:
081: /** Abstract mapping flag. If this is set the marshalling code will call the
082: special interface method used to verify the type of a passed object and
083: marshal it with the proper handling. */
084: private final boolean m_isAbstract;
085:
086: /** Element name information (<code>null</code> if no bound element). */
087: private final NameDefinition m_name;
088:
089: /** Flag for marshaller/unmarshaller slot defined. This is done during code
090: generation, rather than during linking, so that all mapped bindings can be
091: defined with lower index numbers than for marshallers/unmarshallers used
092: only in a specific context. */
093: private boolean m_isSlotSet;
094:
095: /** Marshaller/unmarshaller slot number. */
096: private int m_mumSlot;
097:
098: /** Class handled by this binding. */
099: private final ClassFile m_targetClass;
100:
101: /** Marshaller base class. */
102: private final ClassFile m_marshallerBase;
103:
104: /** Unmarshaller base class. */
105: private final ClassFile m_unmarshallerBase;
106:
107: /** Marshaller class (lazy create on first use if name supplied). */
108: private ClassFile m_marshaller;
109:
110: /** Unmarshaller class (lazy create on first use if name supplied). */
111: private ClassFile m_unmarshaller;
112:
113: /**
114: * Constructor.
115: *
116: * @param parent containing binding definition structure
117: * @param target class handled by this binding
118: * @param abs abstract mapping flag
119: * @param mcf marshaller class information (<code>null</code> if input only
120: * binding)
121: * @param ucf unmarshaller class information (<code>null</code> if output
122: * only binding)
123: * @param slot marshaller/unmarshaller slot number (<code>-1</code> if to be
124: * defined later)
125: * @param name element name information (<code>null</code> if no element
126: * name)
127: * @throws JiBXException if configuration error
128: */
129:
130: public DirectObject(IContainer parent, DefinitionContext defc,
131: ClassFile target, boolean abs, ClassFile mcf,
132: ClassFile ucf, int slot, NameDefinition name)
133: throws JiBXException {
134:
135: // initialize the basic information
136: m_parent = parent;
137: m_defContext = (defc == null) ? m_parent.getDefinitionContext()
138: : defc;
139: m_isAbstract = abs;
140: m_targetClass = target;
141: m_marshallerBase = mcf;
142: m_unmarshallerBase = ucf;
143: m_name = name;
144: m_mumSlot = slot;
145: m_isSlotSet = slot >= 0;
146:
147: // check for marshaller and/or unmarshaller configuration valid
148: if (name == null) {
149:
150: // no name, make sure there's an appropriate constructor
151: if (mcf != null) {
152: if (mcf.getInitializerMethod(ANY_INIT_SIG) != null) {
153: m_marshaller = mcf;
154: } else if (mcf.getInitializerMethod(ANY_INITCLASS_SIG) == null) {
155: throw new JiBXException("Marshaller class "
156: + mcf.getName()
157: + " requires name to be set");
158: }
159: }
160: if (ucf != null) {
161: if (ucf.getInitializerMethod(ANY_INIT_SIG) != null) {
162: m_unmarshaller = ucf;
163: } else if (ucf.getInitializerMethod(ANY_INITCLASS_SIG) == null) {
164: throw new JiBXException("Unmarshaller class "
165: + ucf.getName()
166: + " requires name to be set");
167: }
168: }
169: }
170: if (name != null) {
171:
172: // name supplied, make sure both support it
173: if (mcf != null
174: && !mcf.isImplements(ALIASABLE_INTERFACETYPE)) {
175: throw new JiBXException("Marshaller class "
176: + mcf.getName()
177: + " does not allow name to be set");
178: }
179: if (ucf != null
180: && !ucf.isImplements(ALIASABLE_INTERFACETYPE)) {
181: throw new JiBXException("Unmarshaller class "
182: + ucf.getName()
183: + " does not allow name to be set");
184: }
185: }
186: }
187:
188: /**
189: * Get marshaller/unmarshaller index. This is the index into the tables of
190: * marshaller and unmarshaller instances maintained by the respective
191: * contexts. On the first time call this gets the index number from the
192: * binding definition, then the index number is reused on subsequent calls.
193: *
194: * @return slot number for binding
195: * @throws JiBXException on configuration error
196: */
197:
198: private int getSlot() throws JiBXException {
199: if (!m_isSlotSet) {
200:
201: // first set slot in case called recursively
202: BindingDefinition bdef = m_parent.getBindingRoot();
203: m_mumSlot = bdef
204: .getMarshallerUnmarshallerIndex(m_targetClass
205: .getName());
206: m_isSlotSet = true;
207:
208: // now generate the classes (if needed) and set the names
209: String mclas = null;
210: String uclas = null;
211: if (bdef.isOutput()) {
212: if (m_marshaller == null) {
213: createSubclass(true);
214: }
215: mclas = m_marshaller.getName();
216: }
217: if (bdef.isInput()) {
218: if (m_unmarshaller == null) {
219: createSubclass(false);
220: }
221: uclas = m_unmarshaller.getName();
222: }
223: bdef.setMarshallerUnmarshallerClasses(m_mumSlot, mclas,
224: uclas);
225: }
226: return m_mumSlot;
227: }
228:
229: /**
230: * Load marshaller/unmarshaller index. This is assigned by the binding
231: * definition and used thereafter.
232: *
233: * @param mb method builder
234: */
235:
236: private void genLoadSlot(ContextMethodBuilder mb)
237: throws JiBXException {
238: mb.appendLoadConstant(getSlot());
239: }
240:
241: /**
242: * Create aliased subclass for marshaller or unmarshaller with element name
243: * defined by binding. If the same aliasable superclass is defined for use
244: * as both a marshaller and an unmarshaller a single subclass is generated
245: * to handle both uses.
246: *
247: * @param out <code>true</code> if alias needed for marshalling,
248: * <code>false</code> if for unmarshalling
249: * @throws JiBXException on configuration error
250: */
251:
252: private void createSubclass(boolean out) throws JiBXException {
253:
254: // find initializer call to be used
255: ClassItem init = null;
256: boolean dual = false;
257: boolean classed = true;
258: boolean named = false;
259: ClassFile base = out ? m_marshallerBase : m_unmarshallerBase;
260:
261: // check for name supplied
262: if (m_name == null) {
263: init = base.getInitializerMethod(ANY_INITCLASS_SIG);
264: classed = init != null;
265: }
266: if (init == null) {
267: named = true;
268: if (m_unmarshallerBase == m_marshallerBase) {
269:
270: // single class, look first for signature with class name
271: init = base
272: .getInitializerMethod(MARSHALUNMARSHAL_INITCLASS_SIG);
273: if (init == null) {
274: classed = false;
275: init = base
276: .getInitializerMethod(MARSHALUNMARSHAL_INIT_SIG);
277: }
278: dual = true;
279:
280: } else {
281:
282: // using only one function, first check one way with class name
283: String sig = out ? MARSHALONLY_INITCLASS_SIG
284: : UNMARSHALONLY_INITCLASS_SIG;
285: init = base.getInitializerMethod(sig);
286: if (init == null) {
287:
288: // now check dual function with class name
289: sig = MARSHALUNMARSHAL_INITCLASS_SIG;
290: init = base.getInitializerMethod(sig);
291: dual = true;
292: if (init == null) {
293:
294: // now try alternatives without class name included
295: classed = false;
296: sig = out ? MARSHALONLY_INIT_SIG
297: : UNMARSHALONLY_INIT_SIG;
298: init = base.getInitializerMethod(sig);
299: dual = false;
300: if (init == null) {
301: sig = MARSHALUNMARSHAL_INIT_SIG;
302: init = base.getInitializerMethod(sig);
303: dual = true;
304: }
305:
306: }
307: }
308: }
309: }
310:
311: // make sure the initializer is defined and public
312: if (init == null
313: || ((init.getAccessFlags() & Constants.ACC_PUBLIC) == 0)) {
314: throw new JiBXException("No usable constructor for "
315: + "marshaller or unmarshaller based on "
316: + base.getName());
317: }
318:
319: // get package and target name from bound class
320: String tname = base.getName();
321: int split = tname.lastIndexOf('.');
322: if (split >= 0) {
323: tname = tname.substring(split + 1);
324: }
325:
326: // create the helper class
327: BindingDefinition def = m_parent.getBindingRoot();
328: String pack = def.getDefaultPackage();
329: if (pack.length() > 0) {
330: pack += '.';
331: }
332: String name = pack + def.getPrefix() + tname + '_' + getSlot();
333: String[] intfs = def.isInput() ? (def.isOutput() ? MappingDefinition.BOTH_INTERFACES
334: : MappingDefinition.UNMARSHALLER_INTERFACES)
335: : MappingDefinition.MARSHALLER_INTERFACES;
336: ClassFile cf = new ClassFile(name, def.getDefaultRoot(), base,
337: Constants.ACC_PUBLIC, intfs);
338:
339: // add the public constructor method
340: ExceptionMethodBuilder mb = new ExceptionMethodBuilder(
341: "<init>", Type.VOID, new Type[0], cf,
342: Constants.ACC_PUBLIC);
343:
344: // call the superclass constructor
345: mb.appendLoadLocal(0);
346: if (m_name == null) {
347: if (named) {
348: if (dual) {
349: mb.appendACONST_NULL();
350: mb.appendICONST_0();
351: mb.appendACONST_NULL();
352: } else if (out) {
353: mb.appendICONST_0();
354: mb.appendACONST_NULL();
355: } else {
356: mb.appendACONST_NULL();
357: mb.appendACONST_NULL();
358: }
359: }
360: } else {
361: if (dual) {
362: m_name.genPushUri(mb);
363: m_name.genPushIndexPair(mb);
364: } else if (out) {
365: m_name.genPushIndexPair(mb);
366: } else {
367: m_name.genPushUriPair(mb);
368: }
369: }
370: if (classed) {
371: mb.appendLoadConstant(m_targetClass.getName());
372: }
373: mb.appendCallInit(base.getName(), init.getSignature());
374:
375: // finish with return
376: mb.appendReturn();
377: mb.codeComplete(false);
378:
379: // add method and class
380: mb.addMethod();
381: cf = MungedClass.getUniqueSupportClass(cf);
382:
383: // save as appropriate type(s)
384: if (dual) {
385: m_marshaller = m_unmarshaller = cf;
386: } else if (out) {
387: m_marshaller = cf;
388: } else {
389: m_unmarshaller = cf;
390: }
391: }
392:
393: /**
394: * Generate presence test code for this mapping. The generated code finds
395: * the unmarshaller and calls the test method, leaving the result on the
396: * stack.
397: *
398: * @param mb method builder
399: * @throws JiBXException if error in generating code
400: */
401:
402: public void genTestPresent(ContextMethodBuilder mb)
403: throws JiBXException {
404:
405: // start with call to unmarshalling context method to get the
406: // unmarshaller instance
407: mb.loadContext();
408: genLoadSlot(mb);
409: mb.appendCallInterface(GETUNMARSHALLER_METHOD,
410: GETUNMARSHALLER_SIGNATURE);
411:
412: // call the actual unmarshaller test method with context as argument
413: mb.loadContext();
414: mb.appendCallInterface(UNMARSHALLER_TESTPRESENT_METHOD,
415: UNMARSHALLER_TESTPRESENT_SIGNATURE);
416: }
417:
418: /**
419: * Generate unmarshalling code for this mapping. The generated code finds
420: * and calls the unmarshaller with the object to be unmarshaller (which
421: * needs to be loaded on the stack by the code prior to this call, but may
422: * be <code>null</code>). The unmarshalled object (or <code>null</code> in
423: * the case of a missing optional item) is left on the stack after this
424: * call. The calling method generally needs to cast this object reference to
425: * the appropriate type before using it.
426: *
427: * @param mb method builder
428: * @throws JiBXException if error in generating code
429: */
430:
431: public void genUnmarshal(ContextMethodBuilder mb)
432: throws JiBXException {
433:
434: // start with call to unmarshalling context method to get the
435: // unmarshaller instance
436: mb.loadContext();
437: genLoadSlot(mb);
438: mb.appendCallInterface(GETUNMARSHALLER_METHOD,
439: GETUNMARSHALLER_SIGNATURE);
440:
441: // call the actual unmarshaller with object and context as arguments
442: mb.appendSWAP();
443: mb.loadContext();
444: mb.appendCallInterface(UNMARSHALLER_UNMARSHAL_METHOD,
445: UNMARSHALLER_UNMARSHAL_SIGNATURE);
446: }
447:
448: /**
449: * Generate marshalling code for this mapping. The generated code finds
450: * and calls the marshaller, passing the object to be marshalled (which
451: * should have been loaded to the stack by the prior generated code)..
452: *
453: * @param mb method builder
454: * @throws JiBXException if error in configuration
455: */
456:
457: public void genMarshal(ContextMethodBuilder mb)
458: throws JiBXException {
459:
460: // start with call to marshalling context method to get the marshaller
461: // instance
462: mb.loadContext();
463: genLoadSlot(mb);
464: mb.appendLoadConstant(m_targetClass.getName());
465: mb.appendCallInterface(GETMARSHALLER_METHOD,
466: GETMARSHALLER_SIGNATURE);
467:
468: // check for an abstract mapping being used
469: if (m_isAbstract) {
470:
471: // make sure returned marshaller implements required interface
472: mb.appendCreateCast(ABSTRACTMARSHALLER_INTERFACE);
473:
474: // swap to reorder object and marshaller
475: mb.appendSWAP();
476:
477: // call indirect marshaller for abstract base class with the object
478: // itself and context as arguments
479: mb.loadContext();
480: mb.appendCallInterface(ABSTRACTMARSHAL_METHOD,
481: ABSTRACTMARSHAL_SIGNATURE);
482:
483: } else {
484:
485: // swap to reorder object and marshaller
486: mb.appendSWAP();
487:
488: // call the direct marshaller with the object itself and context as
489: // arguments
490: mb.loadContext();
491: mb.appendCallInterface(MARSHALLER_MARSHAL_METHOD,
492: MARSHALLER_MARSHAL_SIGNATURE);
493: }
494: }
495:
496: /**
497: * Get target class for mapping.
498: *
499: * @return target class information
500: */
501:
502: public ClassFile getTargetClass() {
503: return m_targetClass;
504: }
505:
506: /**
507: * Get marshaller class used for mapping. If a name has been supplied the
508: * actual marshaller class is created by extending the base class the first
509: * time this method is called.
510: *
511: * @return marshaller class information
512: * @throws JiBXException if error in transformation
513: */
514:
515: public ClassFile getMarshaller() throws JiBXException {
516: if (m_marshaller == null && m_marshallerBase != null) {
517: createSubclass(true);
518: }
519: return m_marshaller;
520: }
521:
522: /**
523: * Get unmarshaller class used for mapping. If a name has been supplied the
524: * actual unmarshaller class is created by extending the base class the
525: * first time this method is called.
526: *
527: * @return unmarshaller class information
528: * @throws JiBXException if error in transformation
529: */
530:
531: public ClassFile getUnmarshaller() throws JiBXException {
532: if (m_unmarshaller == null && m_unmarshallerBase != null) {
533: createSubclass(false);
534: }
535: return m_unmarshaller;
536: }
537:
538: //
539: // IComponent interface method definitions
540:
541: public boolean isOptional() {
542: return false;
543: }
544:
545: public boolean hasAttribute() {
546: return false;
547: }
548:
549: public void genAttrPresentTest(ContextMethodBuilder mb) {
550: throw new IllegalStateException(
551: "Internal error - no attributes defined");
552: }
553:
554: public void genAttributeUnmarshal(ContextMethodBuilder mb) {
555: throw new IllegalStateException(
556: "Internal error - no attributes defined");
557: }
558:
559: public void genAttributeMarshal(ContextMethodBuilder mb) {
560: throw new IllegalStateException(
561: "Internal error - no attributes defined");
562: }
563:
564: public boolean hasContent() {
565: return true;
566: }
567:
568: public void genContentPresentTest(ContextMethodBuilder mb)
569: throws JiBXException {
570: genTestPresent(mb);
571: }
572:
573: public void genContentUnmarshal(ContextMethodBuilder mb)
574: throws JiBXException {
575: genUnmarshal(mb);
576: }
577:
578: public void genContentMarshal(ContextMethodBuilder mb)
579: throws JiBXException {
580: genMarshal(mb);
581: }
582:
583: public void genNewInstance(ContextMethodBuilder mb) {
584: throw new IllegalStateException(
585: "Internal error - no instance creation");
586: }
587:
588: public String getType() {
589: return m_targetClass.getFile().getName();
590: }
591:
592: public boolean hasId() {
593: return false;
594: }
595:
596: public void genLoadId(ContextMethodBuilder mb) {
597: throw new IllegalStateException(
598: "Internal error - no ID allowed");
599: }
600:
601: public NameDefinition getWrapperName() {
602: return m_name;
603: }
604:
605: public void setLinkages() throws JiBXException {
606: if (m_name != null) {
607: m_name.fixNamespace(m_defContext);
608: }
609: }
610:
611: // DEBUG
612: public void print(int depth) {
613: BindingDefinition.indent(depth);
614: System.out.println("direct marshaller/unmarshaller reference");
615: }
616: }
|