1 /*
2 * $Header: /home/cvs/jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/io/AbstractBeanWriter.java,v 1.10 2003/01/06 22:50:44 rdonkin Exp $
3 * $Revision: 1.10 $
4 * $Date: 2003/01/06 22:50:44 $
5 *
6 * ====================================================================
7 *
8 * The Apache Software License, Version 1.1
9 *
10 * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
11 * reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 *
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in
22 * the documentation and/or other materials provided with the
23 * distribution.
24 *
25 * 3. The end-user documentation included with the redistribution, if
26 * any, must include the following acknowlegement:
27 * "This product includes software developed by the
28 * Apache Software Foundation (http://www.apache.org/)."
29 * Alternately, this acknowlegement may appear in the software itself,
30 * if and wherever such third-party acknowlegements normally appear.
31 *
32 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
33 * Foundation" must not be used to endorse or promote products derived
34 * from this software without prior written permission. For written
35 * permission, please contact apache@apache.org.
36 *
37 * 5. Products derived from this software may not be called "Apache"
38 * nor may "Apache" appear in their names without prior written
39 * permission of the Apache Group.
40 *
41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 * ====================================================================
54 *
55 * This software consists of voluntary contributions made by many
56 * individuals on behalf of the Apache Software Foundation. For more
57 * information on the Apache Software Foundation, please see
58 * <http://www.apache.org/>.
59 *
60 * $Id: AbstractBeanWriter.java,v 1.10 2003/01/06 22:50:44 rdonkin Exp $
61 */
62 package org.apache.commons.betwixt.io;
63
64 import java.beans.IntrospectionException;
65 import java.io.IOException;
66 import java.util.HashMap;
67 import java.util.Iterator;
68
69 import org.apache.commons.betwixt.AttributeDescriptor;
70 import org.apache.commons.betwixt.ElementDescriptor;
71 import org.apache.commons.betwixt.XMLBeanInfo;
72 import org.apache.commons.betwixt.XMLIntrospector;
73 import org.apache.commons.betwixt.expression.Context;
74 import org.apache.commons.betwixt.expression.Expression;
75 import org.apache.commons.betwixt.io.id.SequentialIDGenerator;
76 import org.apache.commons.collections.ArrayStack;
77 import org.apache.commons.logging.Log;
78 import org.apache.commons.logging.LogFactory;
79 import org.xml.sax.SAXException;
80
81 // FIX ME!!!
82 // Logging logic!
83
84 // FIX ME!!
85 // Error handling strategy!
86 // i'm going to add SAXExceptions everywhere since it's the easiest way to make things work quick
87 // but this is a poor strategy
88
89 /***
90 * <p>Abstract superclass for bean writers.
91 * This class encapsulates the processing logic.
92 * Subclasses provide implementations for the actual expression of the xml.</p>
93 *
94 * @author <a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a>
95 * @version $Revision: 1.10 $
96 */
97 public abstract class AbstractBeanWriter {
98
99 /*** Introspector used */
100 private XMLIntrospector introspector = new XMLIntrospector();
101
102 /*** Log used for logging (Doh!) */
103 private Log log = LogFactory.getLog( AbstractBeanWriter.class );
104 /*** Map containing ID attribute values for beans */
105 private HashMap idMap = new HashMap();
106 /*** Stack containing beans - used to detect cycles */
107 private ArrayStack beanStack = new ArrayStack();
108 /*** Used to generate ID attribute values*/
109 private IDGenerator idGenerator = new SequentialIDGenerator();
110 /*** Should generated <code>ID</code> attribute values be added to the elements? */
111 private boolean writeIDs = true;
112
113 /*** indentation level */
114 private int indentLevel;
115
116 /***
117 * Marks the start of the bean writing.
118 * By default doesn't do anything, but can be used
119 * to do extra start processing
120 * @throws IOException if an IO problem occurs during writing
121 * @throws SAXException if an SAX problem occurs during writing
122 */
123 public void start() throws IOException, SAXException {
124 }
125
126 /***
127 * Marks the start of the bean writing.
128 * By default doesn't do anything, but can be used
129 * to do extra end processing
130 * @throws IOException if an IO problem occurs during writing
131 * @throws SAXException if an SAX problem occurs during writing
132 */
133
134 public void end() throws IOException, SAXException {
135 }
136
137 /***
138 * <p> Writes the given bean to the current stream using the XML introspector.</p>
139 *
140 * <p> This writes an xml fragment representing the bean to the current stream.</p>
141 *
142 * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
143 * is encountered in the graph <strong>only</strong> if the <code>WriteIDs</code>
144 * property is false.</p>
145 *
146 * @throws CyclicReferenceException when a cyclic reference is encountered
147 * @throws IOException if an IO problem occurs during writing
148 * @throws SAXException if an SAX problem occurs during writing
149 * @throws IntrospectionException if a java beans introspection problem occurs
150 *
151 * @param bean write out representation of this bean
152 */
153 public void write(Object bean) throws
154 IOException,
155 SAXException,
156 IntrospectionException {
157 if (log.isDebugEnabled()) {
158 log.debug( "Writing bean graph..." );
159 log.debug( bean );
160 }
161 start();
162 write( null, bean );
163 end();
164 if (log.isDebugEnabled()) {
165 log.debug( "Finished writing bean graph." );
166 }
167 }
168
169 /***
170 * <p>Writes the given bean to the current stream
171 * using the given <code>qualifiedName</code>.</p>
172 *
173 * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
174 * is encountered in the graph <strong>only</strong> if the <code>WriteIDs</code>
175 * property is false.</p>
176 *
177 * @param qualifiedName the string naming root element
178 * @param bean the <code>Object</code> to write out as xml
179 *
180 * @throws CyclicReferenceException when a cyclic reference is encountered
181 * @throws IOException if an IO problem occurs during writing
182 * @throws SAXException if an SAX problem occurs during writing
183 * @throws IntrospectionException if a java beans introspection problem occurs
184 */
185 public void write(
186 String qualifiedName,
187 Object bean)
188 throws
189 IOException,
190 SAXException,
191 IntrospectionException {
192
193
194 if ( log.isTraceEnabled() ) {
195 log.trace( "Writing bean graph (qualified name '" + qualifiedName + "'" );
196 }
197
198 // introspect to obtain bean info
199 XMLBeanInfo beanInfo = introspector.introspect( bean );
200 if ( beanInfo != null ) {
201 ElementDescriptor elementDescriptor = beanInfo.getElementDescriptor();
202 if ( elementDescriptor != null ) {
203 Context context = new Context( bean, log );
204 if ( qualifiedName == null ) {
205 qualifiedName = elementDescriptor.getQualifiedName();
206 }
207
208 String ref = null;
209 String id = null;
210
211 // only give id's to non-primatives
212 if ( elementDescriptor.isPrimitiveType() ) {
213 // write without an id
214 write(
215 qualifiedName,
216 elementDescriptor,
217 context );
218 } else {
219 pushBean ( context.getBean() );
220 if ( writeIDs ) {
221 ref = (String) idMap.get( context.getBean() );
222 }
223 if ( ref == null ) {
224 // this is the first time that this bean has be written
225 AttributeDescriptor idAttribute = beanInfo.getIDAttribute();
226 if (idAttribute == null) {
227 // use a generated id
228 id = idGenerator.nextId();
229 idMap.put( bean, id );
230
231 if ( writeIDs ) {
232 // write element with id
233 write(
234 qualifiedName,
235 elementDescriptor,
236 context ,
237 beanInfo.getIDAttributeName(),
238 id);
239
240 } else {
241 // write element without ID
242 write(
243 qualifiedName,
244 elementDescriptor,
245 context );
246 }
247
248 } else {
249 // use id from bean property
250 // it's up to the user to ensure uniqueness
251 // XXX should we trap nulls?
252 Object exp = idAttribute.getTextExpression().evaluate( context );
253 if (exp == null) {
254 // we'll use a random id
255 log.debug("Using random id");
256 id = idGenerator.nextId();
257
258 } else {
259 // convert to string
260 id = exp.toString();
261 }
262 idMap.put( bean, id);
263
264 // the ID attribute should be written automatically
265 write(
266 qualifiedName,
267 elementDescriptor,
268 context );
269 }
270 } else {
271
272 // we've already written this bean so write an IDREF
273 writeIDREFElement(
274 qualifiedName,
275 beanInfo.getIDREFAttributeName(),
276 ref);
277 }
278 popBean();
279 }
280 }
281 }
282
283 log.trace( "Finished writing bean graph." );
284 }
285
286 /***
287 * Get <code>IDGenerator</code> implementation used to
288 * generate <code>ID</code> attribute values .
289 *
290 * @return implementation used for <code>ID</code> attribute generation
291 */
292 public IDGenerator getIdGenerator() {
293 return idGenerator;
294 }
295
296 /***
297 * Get the indentation for the current element.
298 * Used for pretty priting.
299 *
300 * @return the amount that the current element is indented
301 */
302 protected int getIndentLevel() {
303 return indentLevel;
304 }
305
306 /***
307 * Set <code>IDGenerator</code> implementation
308 * used to generate <code>ID</code> attribute values.
309 * This property can be used to customize the algorithm used for generation.
310 *
311 * @param idGenerator use this implementation for <code>ID</code> attribute generation
312 */
313 public void setIdGenerator(IDGenerator idGenerator) {
314 this.idGenerator = idGenerator;
315 }
316
317 /***
318 * <p>Should generated <code>ID</code> attribute values be added to the elements?</p>
319 *
320 * <p>If IDs are not being written then if a cycle is encountered in the bean graph,
321 * then a {@link CyclicReferenceException} will be thrown by the write method.</p>
322 *
323 * @return true if <code>ID</code> and <code>IDREF</code> attributes are to be written
324 */
325 public boolean getWriteIDs() {
326 return writeIDs;
327 }
328
329 /***
330 * Set whether generated <code>ID</code> attribute values should be added to the elements
331 * If this property is set to false, then <code>CyclicReferenceException</code>
332 * will be thrown whenever a cyclic occurs in the bean graph.
333 *
334 * @param writeIDs true if <code>ID</code>'s and <code>IDREF</code>'s should be written
335 */
336 public void setWriteIDs(boolean writeIDs) {
337 this.writeIDs = writeIDs;
338 }
339
340 /***
341 * <p>Gets the introspector used.</p>
342 *
343 * <p>The {@link XMLBeanInfo} used to map each bean is
344 * created by the <code>XMLIntrospector</code>.
345 * One way in which the mapping can be customized is
346 * by altering the <code>XMLIntrospector</code>. </p>
347 *
348 * @return the <code>XMLIntrospector</code> used for introspection
349 */
350 public XMLIntrospector getXMLIntrospector() {
351 return introspector;
352 }
353
354
355 /***
356 * <p>Sets the introspector to be used.</p>
357 *
358 * <p>The {@link XMLBeanInfo} used to map each bean is
359 * created by the <code>XMLIntrospector</code>.
360 * One way in which the mapping can be customized is by
361 * altering the <code>XMLIntrospector</code>. </p>
362 *
363 * @param introspector use this introspector
364 */
365 public void setXMLIntrospector(XMLIntrospector introspector) {
366 this.introspector = introspector;
367 }
368
369 /***
370 * <p>Gets the current logging implementation.</p>
371 *
372 * @return the <code>Log</code> implementation which this class logs to
373 */
374 public final Log getAbstractBeanWriterLog() {
375 return log;
376 }
377
378 /***
379 * <p> Set the current logging implementation. </p>
380 *
381 * @param log <code>Log</code> implementation to use
382 */
383 public final void setAbstractBeanWriterLog(Log log) {
384 this.log = log;
385 }
386
387
388 // Expression methods
389 //-------------------------------------------------------------------------
390
391 /***
392 * Express an element tag start using given qualified name.
393 *
394 * @param qualifiedName the qualified name of the element to be expressed
395 * @throws IOException if an IO problem occurs during writing
396 * @throws SAXException if an SAX problem occurs during writing
397 */
398 protected abstract void expressElementStart(String qualifiedName)
399 throws IOException, SAXException;
400
401 /***
402 * Express a closing tag.
403 *
404 * @throws IOException if an IO problem occurs during writing
405 * @throws SAXException if an SAX problem occurs during writing
406 */
407 protected abstract void expressTagClose() throws IOException, SAXException;
408
409 /***
410 * Express an element end tag (with given name)
411 *
412 * @param qualifiedName the qualified name for the element to be closed
413 *
414 * @throws IOException if an IO problem occurs during writing
415 * @throws SAXException if an SAX problem occurs during writing
416 */
417 protected abstract void expressElementEnd(String qualifiedName)
418 throws IOException, SAXException;
419
420 /***
421 * Express an empty element end.
422 *
423 * @throws IOException if an IO problem occurs during writing
424 * @throws SAXException if an SAX problem occurs during writing
425 */
426 protected abstract void expressElementEnd() throws IOException, SAXException;
427
428 /***
429 * Express body text
430 *
431 * @param text the string to write out as the body of the current element
432 *
433 * @throws IOException if an IO problem occurs during writing
434 * @throws SAXException if an SAX problem occurs during writing
435 */
436 protected abstract void expressBodyText(String text) throws IOException, SAXException;
437
438 /***
439 * Express an attribute
440 *
441 * @param qualifiedName the qualified name of the attribute
442 * @param value the attribute value
443 * @throws IOException if an IO problem occurs during writing
444 * @throws SAXException if an SAX problem occurs during writing
445 */
446 protected abstract void expressAttribute(
447 String qualifiedName,
448 String value)
449 throws
450 IOException,
451 SAXException;
452
453
454 // Implementation methods
455 //-------------------------------------------------------------------------
456
457
458 /***
459 * Writes the given element
460 *
461 * @param qualifiedName qualified name to use for the element
462 * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
463 * @param context the <code>Context</code> to use to evaluate the bean expressions
464 * @throws IOException if an IO problem occurs during writing
465 * @throws SAXException if an SAX problem occurs during writing
466 * @throws IntrospectionException if a java beans introspection problem occurs
467 */
468 protected void write(
469 String qualifiedName,
470 ElementDescriptor elementDescriptor,
471 Context context )
472 throws
473 IOException,
474 SAXException,
475 IntrospectionException {
476
477 if (elementDescriptor.isWrapCollectionsInElement()) {
478 expressElementStart( qualifiedName );
479 }
480
481 writeRestOfElement( qualifiedName, elementDescriptor, context);
482 }
483
484
485
486 /***
487 * Writes the given element adding an ID attribute
488 *
489 * @param qualifiedName qualified name to use for the element
490 * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
491 * @param context the <code>Context</code> to use to evaluate the bean expressions
492 * @param idAttribute the qualified name of the <code>ID</code> attribute
493 * @param idValue the value for the <code>ID</code> attribute
494 * @throws IOException if an IO problem occurs during writing
495 * @throws SAXException if an SAX problem occurs during writing
496 * @throws IntrospectionException if a java beans introspection problem occurs
497 */
498 protected void write(
499 String qualifiedName,
500 ElementDescriptor elementDescriptor,
501 Context context,
502 String idAttribute,
503 String idValue )
504 throws
505 IOException,
506 SAXException,
507 IntrospectionException {
508
509 expressElementStart( qualifiedName );
510
511 expressAttribute( idAttribute, idValue );
512
513 writeRestOfElement( qualifiedName, elementDescriptor, context );
514 }
515
516 /***
517 * Write attributes, child elements and element end
518 *
519 * @param qualifiedName qualified name to use for the element
520 * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
521 * @param context the <code>Context</code> to use to evaluate the bean expressions
522 * @throws IOException if an IO problem occurs during writing
523 * @throws SAXException if an SAX problem occurs during writing
524 * @throws IntrospectionException if a java beans introspection problem occurs
525 */
526 protected void writeRestOfElement(
527 String qualifiedName,
528 ElementDescriptor elementDescriptor,
529 Context context )
530 throws
531 IOException,
532 SAXException,
533 IntrospectionException {
534
535 if ( elementDescriptor.isWrapCollectionsInElement() ) {
536 writeAttributes( elementDescriptor, context );
537 }
538
539 if ( writeContent( elementDescriptor, context ) ) {
540 if ( elementDescriptor.isWrapCollectionsInElement() ) {
541 expressElementEnd( qualifiedName );
542 }
543 } else {
544 if ( elementDescriptor.isWrapCollectionsInElement() ) {
545 expressElementEnd();
546 }
547 }
548 }
549
550
551 /***
552 * Writes an element with a <code>IDREF</code> attribute
553 *
554 * @param qualifiedName of the element with <code>IDREF</code> attribute
555 * @param idrefAttributeName the qualified name of the <code>IDREF</code> attribute
556 * @param idrefAttributeValue the value for the <code>IDREF</code> attribute
557 * @throws IOException if an IO problem occurs during writing
558 * @throws SAXException if an SAX problem occurs during writing
559 * @throws IntrospectionException if a java beans introspection problem occurs
560 */
561 protected void writeIDREFElement(
562 String qualifiedName,
563 String idrefAttributeName,
564 String idrefAttributeValue )
565 throws
566 IOException,
567 SAXException,
568 IntrospectionException {
569
570 // write IDREF element
571 expressElementStart( qualifiedName );
572
573 expressAttribute( idrefAttributeName, idrefAttributeValue );
574
575 expressElementEnd();
576 }
577
578 /***
579 * Writes the element content.
580 *
581 * @param elementDescriptor the <code>ElementDescriptor</code> to write as xml
582 * @param context the <code>Context</code> to use to evaluate the bean expressions
583 * @return true if some content was written
584 * @throws IOException if an IO problem occurs during writing
585 * @throws SAXException if an SAX problem occurs during writing
586 * @throws IntrospectionException if a java beans introspection problem occurs
587 */
588 protected boolean writeContent(
589 ElementDescriptor elementDescriptor,
590 Context context )
591 throws
592 IOException,
593 SAXException,
594 IntrospectionException {
595 ElementDescriptor[] childDescriptors = elementDescriptor.getElementDescriptors();
596 boolean writtenContent = false;
597 if ( childDescriptors != null && childDescriptors.length > 0 ) {
598 // process child elements
599 for ( int i = 0, size = childDescriptors.length; i < size; i++ ) {
600 ElementDescriptor childDescriptor = childDescriptors[i];
601 Context childContext = context;
602 Expression childExpression = childDescriptor.getContextExpression();
603 if ( childExpression != null ) {
604 Object childBean = childExpression.evaluate( context );
605 if ( childBean != null ) {
606 String qualifiedName = childDescriptor.getQualifiedName();
607 // XXXX: should we handle nulls better
608 if ( childBean instanceof Iterator ) {
609 for ( Iterator iter = (Iterator) childBean; iter.hasNext(); ) {
610 Object object = iter.next();
611 if (object == null) {
612 continue;
613 }
614 if ( ! writtenContent ) {
615 writtenContent = true;
616 if (elementDescriptor.isWrapCollectionsInElement()) {
617 expressTagClose();
618 }
619 }
620 ++indentLevel;
621 write( qualifiedName, object );
622 --indentLevel;
623 }
624 } else {
625 if ( ! writtenContent ) {
626 writtenContent = true;
627 expressTagClose();
628 }
629 ++indentLevel;
630 write( qualifiedName, childBean );
631 --indentLevel;
632 }
633 }
634 } else {
635 if ( ! writtenContent ) {
636 writtenContent = true;
637 expressTagClose();
638 }
639 if (childDescriptor.isWrapCollectionsInElement()) {
640 ++indentLevel;
641 }
642
643 write( childDescriptor.getQualifiedName(), childDescriptor, childContext );
644
645 if (childDescriptor.isWrapCollectionsInElement()) {
646 --indentLevel;
647 }
648 }
649 }
650 if ( writtenContent ) {
651 writePrintln();
652 writeIndent();
653 }
654 } else {
655 // evaluate the body text
656 Expression expression = elementDescriptor.getTextExpression();
657 if ( expression != null ) {
658 Object value = expression.evaluate( context );
659 if ( value != null ) {
660 String text = value.toString();
661 if ( text != null && text.length() > 0 ) {
662 if ( ! writtenContent ) {
663 writtenContent = true;
664 expressTagClose();
665 }
666 expressBodyText(text);
667 }
668 }
669 }
670 }
671 return writtenContent;
672 }
673
674 /***
675 * Writes the attribute declarations
676 *
677 * @param elementDescriptor the <code>ElementDescriptor</code> to be written out as xml
678 * @param context the <code>Context</code> to use to evaluation bean expressions
679 * @throws IOException if an IO problem occurs during writing
680 * @throws SAXException if an SAX problem occurs during writing
681 */
682 protected void writeAttributes(
683 ElementDescriptor elementDescriptor,
684 Context context )
685 throws
686 IOException, SAXException {
687 if (!elementDescriptor.isWrapCollectionsInElement()) {
688 return;
689 }
690
691 AttributeDescriptor[] attributeDescriptors = elementDescriptor.getAttributeDescriptors();
692 if ( attributeDescriptors != null ) {
693 for ( int i = 0, size = attributeDescriptors.length; i < size; i++ ) {
694 AttributeDescriptor attributeDescriptor = attributeDescriptors[i];
695 writeAttribute( attributeDescriptor, context );
696 }
697 }
698 }
699
700
701 /***
702 * Writes an attribute declaration
703 *
704 * @param attributeDescriptor the <code>AttributeDescriptor</code> to be written as xml
705 * @param context the <code>Context</code> to use to evaluation bean expressions
706 * @throws IOException if an IO problem occurs during writing
707 * @throws SAXException if an SAX problem occurs during writing
708 */
709 protected void writeAttribute(
710 AttributeDescriptor attributeDescriptor,
711 Context context )
712 throws
713 IOException, SAXException {
714 Expression expression = attributeDescriptor.getTextExpression();
715 if ( expression != null ) {
716 Object value = expression.evaluate( context );
717 if ( value != null ) {
718 String text = value.toString();
719 if ( text != null && text.length() > 0 ) {
720 expressAttribute(attributeDescriptor.getQualifiedName(), text);
721 }
722 }
723 }
724 }
725
726 /***
727 * Writes a empty line.
728 * This implementation does nothing but can be overridden by subclasses.
729 *
730 * @throws IOException if the line cannot be written
731 */
732 protected void writePrintln() throws IOException {}
733
734 /***
735 * Writes an indentation.
736 * This implementation does nothing but can be overridden by subclasses.
737 *
738 * @throws IOException if the indent cannot be written
739 */
740 protected void writeIndent() throws IOException {}
741
742 /***
743 * Pushes the bean onto the ancestry stack.
744 * If IDs are not being written, then check for cyclic references.
745 *
746 * @param bean push this bean onto the ancester stack
747 * @throws CyclicReferenceException if there is an identical bean already on the stack
748 */
749 protected void pushBean( Object bean ) {
750 // check that we don't have a cyclic reference when we're not writing IDs
751 if ( !writeIDs ) {
752 Iterator it = beanStack.iterator();
753 while ( it.hasNext() ) {
754 Object next = it.next();
755 // use absolute equality rather than equals
756 // we're only really bothered if objects are actually the same
757 if ( bean == next ) {
758 if ( log.isDebugEnabled() ) {
759 log.debug("Element stack: ");
760 Iterator debugStack = beanStack.iterator();
761 while ( debugStack.hasNext() ) {
762 log.debug(debugStack.next());
763 }
764 }
765 log.error("Cyclic reference at bean: " + bean);
766 throw new CyclicReferenceException();
767 }
768 }
769 }
770 if (log.isTraceEnabled()) {
771 log.trace( "Pushing onto object stack: " + bean );
772 }
773 beanStack.push( bean );
774 }
775
776 /***
777 * Pops the top bean off from the ancestry stack
778 *
779 * @return the last object pushed onto the ancester stack
780 */
781 protected Object popBean() {
782 Object bean = beanStack.pop();
783 if (log.isTraceEnabled()) {
784 log.trace( "Popped from object stack: " + bean );
785 }
786 return bean;
787 }
788 }
This page was automatically generated by Maven