View Javadoc
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