KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2 Copyright (c) 2003-2004, 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.generic.*;
32
33 import org.jibx.binding.classes.*;
34 import org.jibx.runtime.JiBXException;
35
36 /**
37  * Collection binding definition. This handles one or more child components,
38  * which may be ordered or unordered.
39  *
40  * @author Dennis M. Sosnoski
41  * @version 1.0
42  */

43
44 public class NestedCollection extends NestedBase
45 {
46     /** Fully qualified class name of values from collection. */
47     private final String JavaDoc m_itemType;
48     
49     /** Strategy for generating code to load item from collection. */
50     private final CollectionLoad m_loadStrategy;
51     
52     /** Strategy for generating code to store item to collection. */
53     private final CollectionStore m_storeStrategy;
54     
55     /**
56      * Constructor.
57      *
58      * @param parent containing binding definition context
59      * @param objc current object context
60      * @param ord ordered content flag
61      * @param type fully qualified class name of values from collection (may be
62      * <code>null</code>, if child content present)
63      * @param load collection load code generation strategy
64      * @param store collection store code generation strategy
65      */

66
67     public NestedCollection(IContainer parent, IContextObj objc, boolean ord,
68         String JavaDoc type, CollectionLoad load, CollectionStore store) {
69         super(parent, objc, ord, false);
70         m_itemType = type;
71         m_loadStrategy = load;
72         m_storeStrategy = store;
73     }
74     
75     //
76
// IComponent interface method definitions
77

78     public boolean hasAttribute() {
79         return false;
80     }
81
82     public void genAttrPresentTest(ContextMethodBuilder mb) {
83         throw new IllegalStateException JavaDoc
84             ("Internal error - no attributes present");
85     }
86
87     public void genAttributeUnmarshal(ContextMethodBuilder mb) {
88         throw new IllegalStateException JavaDoc
89             ("Internal error - no attributes present");
90     }
91
92     public void genAttributeMarshal(ContextMethodBuilder mb) {
93         throw new IllegalStateException JavaDoc
94             ("Internal error - no attributes present");
95     }
96
97     public boolean hasContent() {
98         return m_contents.size() > 0;
99     }
100
101     public void genContentUnmarshal(ContextMethodBuilder mb)
102         throws JiBXException {
103         if (m_contents.size() > 0) {
104         
105             // set up common handling and check for ordered or unordered content
106
m_storeStrategy.genStoreInit(mb);
107             BranchWrapper link = null;
108             int count = m_contents.size();
109             if (m_isOrdered) {
110             
111                 // just generate unmarshal code for each component in order
112
for (int i = 0; i < count; i++) {
113                     
114                     // start with branch target for loop and link from last type
115
if (link != null) {
116                         mb.initStackState(link);
117                     }
118                     BranchTarget start = mb.appendTargetNOP();
119                     if (link != null) {
120                         link.setTarget(start, mb);
121                     }
122                     
123                     // if multiple types are included in content, generate code
124
// to check if an element of this component type is present
125
IComponent child = (IComponent)m_contents.get(i);
126                     child.genContentPresentTest(mb);
127                     link = mb.appendIFEQ(this);
128                     
129                     // follow with code to unmarshal the component and store to
130
// collection, ending with loop back to start of this
131
// component
132
child.genContentUnmarshal(mb);
133                     m_storeStrategy.genStoreItem(mb);
134                     mb.appendUnconditionalBranch(this).setTarget(start, mb);
135                 }
136                 
137             } else {
138             
139                 // generate unmarshal loop code that checks for each component,
140
// branching to the next component until one is found and
141
// exiting the loop only when no component is matched
142
BranchTarget first = mb.appendTargetNOP();
143                 for (int i = 0; i < count; i++) {
144                     if (link != null) {
145                         mb.targetNext(link);
146                     }
147                     IComponent child = (IComponent)m_contents.get(i);
148                     child.genContentPresentTest(mb);
149                     link = mb.appendIFEQ(this);
150                     child.genContentUnmarshal(mb);
151                     m_storeStrategy.genStoreItem(mb);
152                     mb.appendUnconditionalBranch(this).setTarget(first, mb);
153                 }
154             }
155             
156             // patch final test failure branch to fall through loop
157
m_storeStrategy.genStoreDone(mb);
158             mb.targetNext(link);
159             
160         } else {
161             throw new IllegalStateException JavaDoc
162                 ("Internal error - no content present");
163         }
164     }
165
166     public void genContentMarshal(ContextMethodBuilder mb)
167         throws JiBXException {
168         if (m_contents.size() > 0) {
169             
170             // set up common handling of unknown item and collection empty
171
BranchWrapper[] ifempties;
172             BranchWrapper link = null;
173             m_loadStrategy.genLoadInit(mb);
174         
175             // check for ordered or unordered content
176
int count = m_contents.size();
177             if (m_isOrdered) {
178             
179                 // generate marshal code for each component type in order, with
180
// an exception generated if the end of the possible component
181
// list is reached with anything left in the collection
182
ifempties = new BranchWrapper[count];
183                 for (int i = 0; i < count; i++) {
184                     
185                     // start generated code with loading next value from
186
// collection
187
if (link != null) {
188                         mb.initStackState(link, 1);
189                     }
190                     BranchTarget start = mb.appendTargetNOP();
191                     ifempties[i] = m_loadStrategy.genLoadItem(mb);
192                     mb.targetNext(link);
193                     
194                     // if multiple types are included in content, append code to
195
// check if item type matches this component
196
IComponent child = (IComponent)m_contents.get(i);
197                     String JavaDoc type = child.getType();
198                     if (count > 1 || !"java.lang.Object".equals(type)) {
199                         mb.appendDUP();
200                         mb.appendInstanceOf(type);
201                         link = mb.appendIFEQ(this);
202                         mb.appendCreateCast(type);
203                     }
204                     
205                     // finish with code to marshal the component, looping back
206
// to start of block for more of same type
207
child.genContentMarshal(mb);
208                     mb.appendUnconditionalBranch(this).setTarget(start, mb);
209                 }
210                 
211                 // set fall throughs when done with collection
212
mb.targetNext(link);
213                 
214             } else {
215             
216                 // generate marshal loop code that unmarshals an item from the
217
// collection and then checks to see if it matches a component
218
// type, branching to the next component until a match is found
219
// (or generating an exception on no match)
220
BranchTarget start = mb.appendTargetNOP();
221                 ifempties = new BranchWrapper[1];
222                 ifempties[0] = m_loadStrategy.genLoadItem(mb);
223                 for (int i = 0; i < count; i++) {
224                     
225                     // start by setting target for branch from last component
226
mb.targetNext(link);
227                     
228                     // if multiple types are included in content, append code to
229
// check if item type matches this component
230
IComponent child = (IComponent)m_contents.get(i);
231                     String JavaDoc type = child.getType();
232                     if (count > 1 || !"java.lang.Object".equals(type)) {
233                         mb.appendDUP();
234                         mb.appendInstanceOf(type);
235                         link = mb.appendIFEQ(this);
236                         mb.appendCreateCast(type);
237                     }
238                     
239                     // finish with code to marshal the component, branching back
240
// to start of loop
241
child.genContentMarshal(mb);
242                     mb.appendUnconditionalBranch(this).setTarget(start, mb);
243                 }
244             
245             }
246         
247             // patch final test failure branch to generate an exception
248
if (link != null) {
249             
250                 // instruction sequence for exception is create new exception
251
// object, build message in StringBuffer using type of item
252
// from stack, convert StringBuffer to String, invoke the
253
// exeception constructor with the String, and finally throw
254
// the exception
255
mb.targetNext(link);
256                 mb.appendCreateNew("java.lang.StringBuffer");
257                 mb.appendDUP();
258                 mb.appendLoadConstant("Collection item of type ");
259                 mb.appendCallInit("java.lang.StringBuffer",
260                     "(Ljava/lang/String;)V");
261                 mb.appendSWAP();
262                 mb.appendDUP();
263                 BranchWrapper ifnull = mb.appendIFNULL(this);
264                 mb.appendCallVirtual("java.lang.Object.getClass",
265                     "()Ljava/lang/Class;");
266                 mb.appendCallVirtual("java.lang.Class.getName",
267                     "()Ljava/lang/String;");
268                 BranchWrapper toend = mb.appendUnconditionalBranch(this);
269                 mb.targetNext(ifnull);
270                 mb.appendPOP();
271                 mb.appendLoadConstant("NULL");
272                 mb.targetNext(toend);
273                 mb.appendCallVirtual("java.lang.StringBuffer.append",
274                     "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
275                 mb.appendLoadConstant(" has no binding defined");
276                 mb.appendCallVirtual("java.lang.StringBuffer.append",
277                     "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
278                 mb.appendCallVirtual("java.lang.StringBuffer.toString",
279                     "()Ljava/lang/String;");
280                 mb.appendCreateNew(MethodBuilder.FRAMEWORK_EXCEPTION_CLASS);
281                 mb.appendDUP_X1();
282                 mb.appendSWAP();
283                 mb.appendCallInit(MethodBuilder.FRAMEWORK_EXCEPTION_CLASS,
284                     MethodBuilder.EXCEPTION_CONSTRUCTOR_SIGNATURE1);
285                 mb.appendThrow();
286             }
287             
288             // finish by setting target for collection empty case(s)
289
m_loadStrategy.genLoadDone(mb);
290             for (int i = 0; i < ifempties.length; i++) {
291                 mb.targetNext(ifempties[i]);
292             }
293             
294         } else {
295             throw new IllegalStateException JavaDoc
296                 ("Internal error - no content present");
297         }
298     }
299     
300     public boolean hasId() {
301         return false;
302     }
303     
304     public void genLoadId(ContextMethodBuilder mb) throws JiBXException {
305         throw new IllegalStateException JavaDoc("No ID child");
306     }
307
308     public boolean checkContentSequence(boolean text) throws JiBXException {
309         
310         // text not allowed as direct component of collection
311
for (int i = 0; i < m_contents.size(); i++) {
312             IComponent content = (IComponent)m_contents.get(i);
313             content.checkContentSequence(false);
314         }
315         return false;
316     }
317
318     public void setLinkages() throws JiBXException {
319         for (int i = 0; i < m_contents.size(); i++) {
320             ((IComponent)m_contents.get(i)).setLinkages();
321         }
322     }
323     
324     // DEBUG
325
public void print(int depth) {
326         BindingDefinition.indent(depth);
327         System.out.print("collection " +
328             (m_isOrdered ? "ordered" : "unordered"));
329         System.out.println();
330         for (int i = 0; i < m_contents.size(); i++) {
331             IComponent comp = (IComponent)m_contents.get(i);
332             comp.print(depth+1);
333         }
334     }
335     
336     /**
337      * Base class for collection item load strategy. The implementation class
338      * must handle the appropriate form of code generation for the type of
339      * collection being used.
340      */

341     
342     /*package*/ static abstract class CollectionLoad
343     {
344         /**
345          * Generate code to initialize collection for loading items. This
346          * generates the necessary code for handling the initialization. It
347          * must be called before attempting to call the {@link #genLoadItem}
348          * method. The base class implementation does nothing.
349          *
350          * @param mb method builder
351          * @throws JiBXException if error in configuration
352          */

353
354         protected void genLoadInit(ContextMethodBuilder mb)
355             throws JiBXException {}
356         
357         /**
358          * Generate code to load next item from collection. This generates the
359          * necessary code for handling the load operation, leaving the item on
360          * the stack. The {@link #genLoadInit} method must be called before
361          * calling this method, and the {@link #genLoadDone} method must be
362          * called after the last call to this method. This method must be
363          * overridden by each subclass.
364          *
365          * @param mb method builder
366          * @return branch wrapper for case of done with collection
367          * @throws JiBXException if error in configuration
368          */

369
370         protected abstract BranchWrapper genLoadItem(ContextMethodBuilder mb)
371             throws JiBXException;
372         
373         /**
374          * Generate code to clean up after loading items from collection. This
375          * generates the necessary code for handling the clean up. It must be
376          * called after the last call to {@link #genLoadItem}. The base class
377          * implementation does nothing.
378          *
379          * @param mb method builder
380          * @throws JiBXException if error in configuration
381          */

382
383         protected void genLoadDone(ContextMethodBuilder mb)
384             throws JiBXException {}
385     }
386     
387     /**
388      * Base class for collection item store strategy. The implementation class
389      * must handle the appropriate form of code generation for the type of
390      * collection being used.
391      */

392     
393     /*package*/ static abstract class CollectionStore
394     {
395         /**
396          * Generate code to initialize collection for storing items. This
397          * generates the necessary code for handling the initialization,
398          * including creating the collection object if appropriate. It must be
399          * called before attempting to call the {@link #genStoreItem} method.
400          * The base class implementation does nothing.
401          *
402          * @param mb method builder
403          * @throws JiBXException if error in configuration
404          */

405
406         protected void genStoreInit(ContextMethodBuilder mb)
407             throws JiBXException {}
408         
409         /**
410          * Generate code to store next item to collection. This generates the
411          * necessary code for handling the store operation, removing the item
412          * from the stack. The {@link #genStoreInit} method must be called
413          * before calling this method, and the {@link #genStoreDone} method must
414          * be called after the last call to this method. This method must be
415          * overridden by each subclass.
416          *
417          * @param mb method builder
418          * @throws JiBXException if error in configuration
419          */

420
421         protected abstract void genStoreItem(ContextMethodBuilder mb)
422             throws JiBXException;
423         
424         /**
425          * Generate code to clean up after storing items to collection. This
426          * generates the necessary code for handling the clean up. It must be
427          * called after the last call to {@link #genStoreItem}. The base class
428          * implementation does nothing.
429          *
430          * @param mb method builder
431          * @throws JiBXException if error in configuration
432          */

433
434         protected void genStoreDone(ContextMethodBuilder mb)
435             throws JiBXException {}
436     }
437     
438     /**
439      * Collection item load strategy for collection with items accessed by
440      * index number.
441      */

442     
443     /*package*/ static class IndexedLoad extends CollectionLoad
444     {
445         /** Method used to get count of items in collection. */
446         private final ClassItem m_sizeMethod;
447         
448         /** Method used to get items by index from collection. */
449         private final ClassItem m_getMethod;
450         
451         /**
452          * Constructor.
453          *
454          * @param size method used to get count of items in collection
455          * @param get method used to retrieve items by index from collection
456          */

457         
458         /*package*/ IndexedLoad(ClassItem size, ClassItem get) {
459             m_sizeMethod = size;
460             m_getMethod = get;
461         }
462         
463         protected void genLoadInit(ContextMethodBuilder mb)
464             throws JiBXException {
465             
466             // create index local with appended code to set initial value
467
mb.appendLoadConstant(-1);
468             mb.defineSlot(m_getMethod, Type.INT);
469             
470             // create size local with appended code to set initial value from
471
// collection method call
472
if (!m_sizeMethod.isStatic()) {
473                 mb.loadObject();
474             }
475             mb.appendCall(m_sizeMethod);
476             mb.defineSlot(m_sizeMethod, Type.INT);
477         }
478
479         protected BranchWrapper genLoadItem(ContextMethodBuilder mb)
480             throws JiBXException {
481             
482             // start by getting local variable slots for the index and size
483
int islot = mb.getSlot(m_getMethod);
484             int sslot = mb.getSlot(m_sizeMethod);
485             
486             // append code to first increment index, then check for end of
487
// collection reached
488
mb.appendIncrementLocal(1, islot);
489             mb.appendLoadLocal(islot);
490             mb.appendLoadLocal(sslot);
491             mb.appendISUB();
492             BranchWrapper ifempty = mb.appendIFGE(this);
493             
494             // finish by calling collection method to load item at current index
495
// position
496
if (!m_getMethod.isStatic()) {
497                 mb.loadObject();
498             }
499             mb.appendLoadLocal(islot);
500             mb.appendCall(m_getMethod);
501             return ifempty;
502         }
503
504         protected void genLoadDone(ContextMethodBuilder mb)
505             throws JiBXException {
506             mb.freeSlot(m_getMethod);
507             mb.freeSlot(m_sizeMethod);
508         }
509     }
510     
511     /**
512      * Collection item store strategy for collection with items set by
513      * index number.
514      */

515     
516     /*package*/ static class IndexedStore extends CollectionStore
517     {
518         /** Method used to get items by index from collection. */
519         private final ClassItem m_setMethod;
520         
521         /** Flag for method returns result. */
522         private final boolean m_isReturned;
523         
524         /**
525          * Constructor.
526          *
527          * @param set method used to store items by index in collection
528          * @param ret value returned by add flag
529          */

530         
531         /*package*/ IndexedStore(ClassItem set, boolean ret) {
532             m_setMethod = set;
533             m_isReturned = ret;
534         }
535         
536         protected void genStoreInit(ContextMethodBuilder mb)
537             throws JiBXException {
538             
539             // create index local with appended code to set initial value
540
mb.appendLoadConstant(-1);
541             mb.defineSlot(m_setMethod, Type.INT);
542         }
543
544         protected void genStoreItem(ContextMethodBuilder mb)
545             throws JiBXException {
546             
547             // start by getting local variable slot for the index
548
int islot = mb.getSlot(m_setMethod);
549             
550             // append code to first load object and swap with item, then
551
// increment index and swap copy with item, and finally call
552
// collection method to store item at new index position
553
if (!m_setMethod.isStatic()) {
554                 mb.loadObject();
555                 mb.appendSWAP();
556             }
557             mb.appendIncrementLocal(1, islot);
558             mb.appendLoadLocal(islot);
559             mb.appendSWAP();
560             mb.appendCall(m_setMethod);
561             if (m_isReturned) {
562                 mb.appendPOP();
563             }
564         }
565
566         protected void genStoreDone(ContextMethodBuilder mb)
567             throws JiBXException {
568             mb.freeSlot(m_setMethod);
569         }
570     }
571     
572     /**
573      * Collection item load strategy for collection with items accessed by
574      * iterator or enumeration.
575      */

576     
577     /*package*/ static class IteratorLoad extends CollectionLoad
578     {
579         /** Method used to get iterator for collection. */
580         private final ClassItem m_iterMethod;
581         
582         /** Fully qualified method name to test if more in iteration. */
583         private final String JavaDoc m_moreName;
584         
585         /** Fully qualified method name to get next item in iteration. */
586         private final String JavaDoc m_nextName;
587         
588         /**
589          * Constructor.
590          *
591          * @param iter method to get iterator or enumerator from collection
592          * @param more fully qualified method name to test if more in iteration
593          * @param next fully qualified method name to get next item in iteration
594          */

595         
596         /*package*/ IteratorLoad(ClassItem iter, String JavaDoc more, String JavaDoc next) {
597             m_iterMethod = iter;
598             m_moreName = more;
599             m_nextName = next;
600         }
601         
602         protected void genLoadInit(ContextMethodBuilder mb)
603             throws JiBXException {
604             
605             // create iterator local with appended code to set initial value
606
if (!m_iterMethod.isStatic()) {
607                 mb.loadObject();
608             }
609             mb.appendCall(m_iterMethod);
610             mb.defineSlot(m_iterMethod, Type.getType("Ljava/util/Iterator;"));
611         }
612
613         protected BranchWrapper genLoadItem(ContextMethodBuilder mb)
614             throws JiBXException {
615             
616             // start with code to load and test iterator
617
int islot = mb.getSlot(m_iterMethod);
618             mb.appendLoadLocal(islot);
619             mb.appendCallInterface(m_moreName, "()Z");
620             BranchWrapper ifempty = mb.appendIFEQ(this);
621             
622             // append code to get next item from iterator
623
mb.appendLoadLocal(islot);
624             mb.appendCallInterface(m_nextName, "()Ljava/lang/Object;");
625             return ifempty;
626         }
627
628         protected void genLoadDone(ContextMethodBuilder mb)
629             throws JiBXException {
630             mb.freeSlot(m_iterMethod);
631         }
632     }
633     
634     /**
635      * Collection item store strategy for collection with add method.
636      */

637     
638     /*package*/ static class AddStore extends CollectionStore
639     {
640         /** Method used to add item to collection. */
641         private final ClassItem m_addMethod;
642         
643         /** Flag for method returns result. */
644         private final boolean m_isReturned;
645         
646         /**
647          * Constructor.
648          *
649          * @param add method used to add item to collection
650          * @param ret value returned by add flag
651          */

652         
653         /*package*/ AddStore(ClassItem add, boolean ret) {
654             m_addMethod = add;
655             m_isReturned = ret;
656         }
657
658         protected void genStoreItem(ContextMethodBuilder mb)
659             throws JiBXException {
660             
661             // append code to call collection method to add the item
662
if (!m_addMethod.isStatic()) {
663                 mb.loadObject();
664                 mb.appendSWAP();
665             }
666             mb.appendCall(m_addMethod);
667             if (m_isReturned) {
668                 mb.appendPOP();
669             }
670         }
671     }
672 }
Popular Tags