Coverage Report - org.apache.commons.betwixt.io.AbstractBeanWriter

Classes in this File Line Coverage Branch Coverage Complexity
AbstractBeanWriter
72% 
81% 
2.292

 1  
 /*
 2  
  * Copyright 2001-2004 The Apache Software Foundation.
 3  
  * 
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */ 
 16  
 package org.apache.commons.betwixt.io;
 17  
 
 18  
 import java.beans.IntrospectionException;
 19  
 import java.io.IOException;
 20  
 import java.util.ArrayList;
 21  
 import java.util.Collection;
 22  
 import java.util.Iterator;
 23  
 
 24  
 import org.apache.commons.betwixt.AttributeDescriptor;
 25  
 import org.apache.commons.betwixt.BindingConfiguration;
 26  
 import org.apache.commons.betwixt.Descriptor;
 27  
 import org.apache.commons.betwixt.ElementDescriptor;
 28  
 import org.apache.commons.betwixt.Options;
 29  
 import org.apache.commons.betwixt.XMLBeanInfo;
 30  
 import org.apache.commons.betwixt.XMLIntrospector;
 31  
 import org.apache.commons.betwixt.expression.Context;
 32  
 import org.apache.commons.betwixt.expression.Expression;
 33  
 import org.apache.commons.betwixt.io.id.SequentialIDGenerator;
 34  
 import org.apache.commons.collections.ArrayStack;
 35  
 import org.apache.commons.logging.Log;
 36  
 import org.apache.commons.logging.LogFactory;
 37  
 import org.xml.sax.Attributes;
 38  
 import org.xml.sax.InputSource;
 39  
 import org.xml.sax.SAXException;
 40  
 import org.xml.sax.helpers.AttributesImpl;
 41  
 
 42  
 /**
 43  
   * <p>Abstract superclass for bean writers.
 44  
   * This class encapsulates the processing logic. 
 45  
   * Subclasses provide implementations for the actual expression of the xml.</p>
 46  
   * <h5>SAX Inspired Writing API</h5>
 47  
   * <p>
 48  
   * This class is intended to be used by subclassing: 
 49  
   * concrete subclasses perform the actual writing by providing
 50  
   * suitable implementations for the following methods inspired 
 51  
   * by <a href='http://www.saxproject.org'>SAX</a>:
 52  
   * </p>
 53  
   * <ul>
 54  
   *     <li> {@link #start} - called when processing begins</li>
 55  
   *     <li> {@link #startElement(WriteContext, String, String, String, Attributes)} 
 56  
   *     - called when the start of an element 
 57  
   *     should be written</li> 
 58  
   *     <li> {@link #bodyText(WriteContext, String)} 
 59  
   *     - called when the start of an element 
 60  
   *     should be written</li> 
 61  
   *     <li> {@link #endElement(WriteContext, String, String, String)} 
 62  
   *     - called when the end of an element 
 63  
   *     should be written</li> 
 64  
   *     <li> {@link #end} - called when processing has been completed</li>
 65  
   * </ul>
 66  
   * <p>
 67  
   * <strong>Note</strong> that this class contains many deprecated 
 68  
   * versions of the writing API. These will be removed soon so care
 69  
   * should be taken to use the latest version.
 70  
   * </p>
 71  
   * <p>
 72  
   * <strong>Note</strong> that this class is designed to be used
 73  
   * in a single threaded environment. When used in multi-threaded
 74  
   * environments, use of a common <code>XMLIntrospector</code>
 75  
   * and pooled writer instances should be considered. 
 76  
   * </p>
 77  
   *
 78  
   * @author <a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a>
 79  
   */
 80  4042
 public abstract class AbstractBeanWriter {
 81  
 
 82  
     /** Introspector used */
 83  2021
     private XMLIntrospector introspector = new XMLIntrospector();
 84  
 
 85  
     /** Log used for logging (Doh!) */
 86  2021
     private Log log = LogFactory.getLog( AbstractBeanWriter.class );
 87  
     /** Stack containing beans - used to detect cycles */
 88  2021
     private ArrayStack beanStack = new ArrayStack();
 89  
     /** Used to generate ID attribute values*/
 90  2021
     private IDGenerator idGenerator = new SequentialIDGenerator();
 91  
     /** Should empty elements be written out? */
 92  2021
     private boolean writeEmptyElements = true;
 93  
     /** Dynamic binding configuration settings */
 94  2021
     private BindingConfiguration bindingConfiguration = new BindingConfiguration();
 95  
     /** <code>WriteContext</code> implementation reused curing writing */
 96  2021
     private WriteContextImpl writeContext = new WriteContextImpl();
 97  
     /** Collection of namespaces which have already been declared */
 98  2021
     private Collection namespacesDeclared = new ArrayList();
 99  
     
 100  
     /**
 101  
      * Marks the start of the bean writing.
 102  
      * By default doesn't do anything, but can be used
 103  
      * to do extra start processing 
 104  
      * @throws IOException if an IO problem occurs during writing 
 105  
      * @throws SAXException if an SAX problem occurs during writing 
 106  
      */
 107  
     public void start() throws IOException, SAXException {
 108  1956
     }
 109  
     
 110  
     /**
 111  
      * Marks the start of the bean writing.
 112  
      * By default doesn't do anything, but can be used
 113  
      * to do extra end processing 
 114  
      * @throws IOException if an IO problem occurs during writing
 115  
      * @throws SAXException if an SAX problem occurs during writing 
 116  
      */
 117  
     
 118  
     public void end() throws IOException, SAXException {
 119  1930
     }
 120  
         
 121  
     /** 
 122  
      * <p> Writes the given bean to the current stream using the XML introspector.</p>
 123  
      * 
 124  
      * <p> This writes an xml fragment representing the bean to the current stream.</p>
 125  
      *
 126  
      * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
 127  
      * is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
 128  
      * setting of the </code>BindingConfiguration</code> is false.</p>
 129  
      *
 130  
      * @throws IOException if an IO problem occurs during writing 
 131  
      * @throws SAXException if an SAX problem occurs during writing  
 132  
      * @throws IntrospectionException if a java beans introspection problem occurs 
 133  
      *
 134  
      * @param bean write out representation of this bean
 135  
      */
 136  
     public void write(Object bean) throws 
 137  
                                         IOException, 
 138  
                                         SAXException, 
 139  
                                         IntrospectionException {
 140  1800
         if (log.isDebugEnabled()) {
 141  0
             log.debug( "Writing bean graph..." );
 142  0
             log.debug( bean );
 143  
         }
 144  1800
         start();
 145  1800
         writeBean( null, null, null, bean, makeContext( bean ) );
 146  1774
         end();
 147  1774
         if (log.isDebugEnabled()) {
 148  0
             log.debug( "Finished writing bean graph." );
 149  
         }
 150  1774
     }
 151  
     
 152  
     /** 
 153  
      * <p>Writes the given bean to the current stream 
 154  
      * using the given <code>qualifiedName</code>.</p>
 155  
      *
 156  
      * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
 157  
      * is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
 158  
      * setting of the <code>BindingConfiguration</code> is false.</p>
 159  
      *
 160  
      * @param qualifiedName the string naming root element
 161  
      * @param bean the <code>Object</code> to write out as xml
 162  
      * 
 163  
      * @throws IOException if an IO problem occurs during writing
 164  
      * @throws SAXException if an SAX problem occurs during writing 
 165  
      * @throws IntrospectionException if a java beans introspection problem occurs
 166  
      */
 167  
     public void write(
 168  
                 String qualifiedName, 
 169  
                 Object bean) 
 170  
                     throws 
 171  
                         IOException, 
 172  
                         SAXException,
 173  
                         IntrospectionException {
 174  221
         start();
 175  221
         writeBean( "", qualifiedName, qualifiedName, bean, makeContext( bean ) );
 176  221
         end();
 177  221
     }
 178  
     
 179  
     /**
 180  
      * <p>Writes the bean using the mapping specified in the <code>InputSource</code>.
 181  
      * </p><p>
 182  
      * <strong>Note:</strong> that the custom mapping will <em>not</em>
 183  
      * be registered for later use. Please use {@link XMLIntrospector#register}
 184  
      * to register the custom mapping for the class and then call
 185  
      * {@link #write(Object)}.
 186  
      * </p>
 187  
      * @see #write(Object) since the standard notes also apply
 188  
      * @since 0.7
 189  
      * @param bean <code>Object</code> to be written as xml, not null
 190  
      * @param source <code>InputSource/code> containing an xml document
 191  
      * specifying the mapping to be used (in the usual way), not null
 192  
      * @throws IOException
 193  
      * @throws SAXException
 194  
      * @throws IntrospectionException
 195  
      */
 196  
     public void write(Object bean, InputSource source) 
 197  
                                             throws IOException, SAXException, IntrospectionException {
 198  26
         writeBean(
 199  13
                         null, 
 200  13
                         null, 
 201  13
                         null, 
 202  13
                         bean, 
 203  13
                         makeContext( bean ), 
 204  13
                         getXMLIntrospector().introspect(bean.getClass(), source));
 205  13
     }
 206  
     
 207  
     /** 
 208  
      * <p>Writes the given bean to the current stream 
 209  
      * using the given <code>qualifiedName</code>.</p>
 210  
      *
 211  
      * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
 212  
      * is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
 213  
      * setting of the <code>BindingConfiguration</code> is false.</p>
 214  
      *
 215  
      * @param namespaceUri the namespace uri
 216  
      * @param localName the local name
 217  
      * @param qualifiedName the string naming root element
 218  
      * @param introspectedBindType the <code>Class</code> of the bean 
 219  
      * as resolved at introspection time, or null if the type has not been resolved
 220  
      * @param bean the <code>Object</code> to write out as xml
 221  
      * @param context not null
 222  
      * 
 223  
      * @throws IOException if an IO problem occurs during writing
 224  
      * @throws SAXException if an SAX problem occurs during writing 
 225  
      * @throws IntrospectionException if a java beans introspection problem occurs
 226  
      */
 227  
     private void writeBean (
 228  
                 String namespaceUri,
 229  
                 String localName,
 230  
                 String qualifiedName, 
 231  
                 Object bean,
 232  
                 Context context) 
 233  
                     throws 
 234  
                         IOException, 
 235  
                         SAXException,
 236  
                         IntrospectionException {                    
 237  
         
 238  2021
         if ( log.isTraceEnabled() ) {
 239  0
             log.trace( "Writing bean graph (qualified name '" + qualifiedName + "'" );
 240  
         }
 241  
         
 242  
         // introspect to obtain bean info
 243  2021
         XMLBeanInfo beanInfo = introspector.introspect( bean );
 244  2021
         writeBean(namespaceUri, localName, qualifiedName, bean, context, beanInfo);
 245  
         
 246  1995
         log.trace( "Finished writing bean graph." );
 247  1995
     }
 248  
     
 249  
     
 250  
     private void writeBean (
 251  
             String namespaceUri,
 252  
             String localName,
 253  
             String qualifiedName, 
 254  
             Object bean,
 255  
             ElementDescriptor parentDescriptor,
 256  
             Context context) 
 257  
                 throws 
 258  
                     IOException, 
 259  
                     SAXException,
 260  
                     IntrospectionException {                    
 261  
     
 262  7266
     if ( log.isTraceEnabled() ) {
 263  0
         log.trace( "Writing bean graph (qualified name '" + qualifiedName + "'" );
 264  
     }
 265  
     
 266  
     // introspect to obtain bean info
 267  7266
     XMLBeanInfo beanInfo = findXMLBeanInfo(bean, parentDescriptor);
 268  7266
     writeBean(namespaceUri, localName, qualifiedName, bean, context, beanInfo);
 269  
     
 270  7136
     log.trace( "Finished writing bean graph." );
 271  7136
 }
 272  
     
 273  
     /**
 274  
      * Finds the appropriate bean info for the given (hollow) element.
 275  
      * @param bean
 276  
      * @param parentDescriptor <code>ElementDescriptor</code>, not null
 277  
      * @return <code>XMLBeanInfo</code>, not null
 278  
      * @throws IntrospectionException
 279  
      */
 280  
     private XMLBeanInfo findXMLBeanInfo(Object bean, ElementDescriptor parentDescriptor) throws IntrospectionException {
 281  7357
         XMLBeanInfo beanInfo = null;
 282  7357
         Class introspectedBindType = parentDescriptor.getSingularPropertyType();
 283  7357
         if ( introspectedBindType == null ) {
 284  98
             introspectedBindType = parentDescriptor.getPropertyType();
 285  
         }
 286  7357
         if ( parentDescriptor.isUseBindTimeTypeForMapping() || introspectedBindType == null ) {
 287  7318
             beanInfo = introspector.introspect( bean );
 288  
         } else {
 289  39
             beanInfo = introspector.introspect( introspectedBindType );
 290  
         }
 291  7357
         return beanInfo;
 292  
     }
 293  
 
 294  
     /**
 295  
      * <p>Writes the given bean to the current stream 
 296  
      * using the given mapping.</p>
 297  
      *
 298  
      * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
 299  
      * is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
 300  
      * setting of the <code>BindingConfiguration</code> is false.</p>
 301  
      *
 302  
      * @param namespaceUri the namespace uri, or null to use the automatic binding
 303  
      * @param localName the local name  or null to use the automatic binding
 304  
      * @param qualifiedName the <code>String</code> naming the root element 
 305  
      *  or null to use the automatic binding
 306  
      * @param bean <code>Object</code> to be written, not null
 307  
      * @param context <code>Context</code>, not null
 308  
      * @param beanInfo <code>XMLBeanInfo</code>, not null
 309  
      * @throws IOException
 310  
      * @throws SAXException
 311  
      * @throws IntrospectionException
 312  
      */
 313  
     private void writeBean(
 314  
                                                     String namespaceUri, 
 315  
                                                     String localName, 
 316  
                                                     String qualifiedName, 
 317  
                                                     Object bean, 
 318  
                                                     Context context, 
 319  
                                                     XMLBeanInfo beanInfo) 
 320  
                                                                             throws IOException, SAXException, IntrospectionException {
 321  9300
         if ( beanInfo != null ) {
 322  9300
             ElementDescriptor elementDescriptor = beanInfo.getElementDescriptor();
 323  9300
             if ( elementDescriptor != null ) {
 324  
                 
 325  
                 // Construct the options
 326  9300
                 Options combinedOptions = new Options();
 327  
 
 328  
                 // Add options defined by the current bean's element descriptor
 329  9300
                 combinedOptions.addOptions(elementDescriptor.getOptions());
 330  
                 
 331  
                 // The parent descriptor may have defined options
 332  
                 // for the current bean.  These options take precedence
 333  
                 // over the options of the current class descriptor
 334  9300
                 if( context.getOptions() != null) {
 335  7266
                     combinedOptions.addOptions(context.getOptions());
 336  
                 }
 337  9300
                 context = context.newContext( bean );
 338  9300
                 context.pushOptions(combinedOptions);
 339  
                 
 340  9300
                 if ( qualifiedName == null ) {
 341  2540
                     qualifiedName = elementDescriptor.getQualifiedName();
 342  
                 }
 343  9300
                 if ( namespaceUri == null ) {
 344  1813
                     namespaceUri = elementDescriptor.getURI();
 345  
                 }
 346  9300
                 if ( localName == null ) {
 347  2618
                     localName = elementDescriptor.getLocalName();
 348  
                 }
 349  
 
 350  9300
                 String ref = null;
 351  9300
                 String id = null;
 352  
                 
 353  
                 // simple type should not have IDs
 354  9300
                 if ( elementDescriptor.isSimple() ) {
 355  
                     // write without an id
 356  2806
                     writeElement( 
 357  1403
                         namespaceUri,
 358  1403
                         localName,
 359  1403
                         qualifiedName, 
 360  1403
                         elementDescriptor, 
 361  1403
                         context );
 362  
                         
 363  
                 } else {
 364  7897
                     pushBean ( context.getBean() );
 365  7871
                     if ( getBindingConfiguration().getMapIDs() ) {
 366  1131
                        ref = getBindingConfiguration().getIdMappingStrategy().getReferenceFor(context, context.getBean());
 367  
                     }
 368  7871
                     if ( ref == null ) {
 369  
                         // this is the first time that this bean has be written
 370  7806
                         AttributeDescriptor idAttribute = beanInfo.getIDAttribute();
 371  7806
                         if (idAttribute == null) {
 372  
                             // use a generated id
 373  7793
                             id = idGenerator.nextId();
 374  7793
                             getBindingConfiguration().getIdMappingStrategy().setReference(context, bean, id);
 375  
                             
 376  7793
                             if ( getBindingConfiguration().getMapIDs() ) {
 377  
                                 // write element with id
 378  2106
                                 writeElement(
 379  1053
                                     namespaceUri,
 380  1053
                                     localName,
 381  1053
                                     qualifiedName, 
 382  1053
                                     elementDescriptor, 
 383  1053
                                     context , 
 384  1053
                                     beanInfo.getIDAttributeName(),
 385  1053
                                     id);
 386  
                                     
 387  
 
 388  
                             } else {    
 389  
                                 // write element without ID
 390  13480
                                 writeElement( 
 391  6740
                                     namespaceUri,
 392  6740
                                     localName,
 393  6740
                                     qualifiedName, 
 394  6740
                                     elementDescriptor, 
 395  6740
                                     context );
 396  
                             }
 397  
                                                         
 398  
                         } else {
 399  
                             // use id from bean property
 400  
                             // it's up to the user to ensure uniqueness
 401  13
                             Expression idExpression = idAttribute.getTextExpression();
 402  13
                             if(idExpression == null) {
 403  0
                                    throw new IntrospectionException(
 404  0
                                          "The specified id property wasn't found in the bean ("
 405  0
                                         + idAttribute + ").");
 406  
                             }
 407  13
                             Object exp = idExpression.evaluate( context );
 408  13
                             if (exp == null) {
 409  
                                 // we'll use a random id
 410  0
                                 log.debug("Using random id");
 411  0
                                 id = idGenerator.nextId();
 412  
                                 
 413  
                             } else {
 414  
                                 // convert to string
 415  13
                                 id = exp.toString();
 416  
                             }
 417  13
                             getBindingConfiguration().getIdMappingStrategy().setReference(context, bean, id);
 418  
                             
 419  
                             // the ID attribute should be written automatically
 420  26
                             writeElement( 
 421  13
                                 namespaceUri,
 422  13
                                 localName,
 423  13
                                 qualifiedName, 
 424  13
                                 elementDescriptor, 
 425  13
                                 context );
 426  
                         }
 427  
                     } else {
 428  
                         
 429  65
                         if ( !ignoreElement( elementDescriptor, namespaceUri, localName, qualifiedName, context )) {
 430  
                             // we've already written this bean so write an IDREF
 431  130
                             writeIDREFElement( 
 432  65
                                             elementDescriptor,
 433  65
                                             namespaceUri,
 434  65
                                             localName,
 435  65
                                             qualifiedName,  
 436  65
                                             beanInfo.getIDREFAttributeName(), 
 437  65
                                             ref);
 438  
                         }
 439  
                     }
 440  7741
                     popBean();
 441  
                 }
 442  
                 
 443  9144
                 context.popOptions();
 444  
             }
 445  
         }
 446  9144
     }
 447  
 
 448  
     /** 
 449  
       * Get <code>IDGenerator</code> implementation used to 
 450  
       * generate <code>ID</code> attribute values .
 451  
       *
 452  
       * @return implementation used for <code>ID</code> attribute generation
 453  
       */
 454  
     public IDGenerator getIdGenerator() {
 455  0
         return idGenerator;
 456  
     }
 457  
     
 458  
     /** 
 459  
       * Set <code>IDGenerator</code> implementation 
 460  
       * used to generate <code>ID</code> attribute values.
 461  
       * This property can be used to customize the algorithm used for generation.
 462  
       *
 463  
       * @param idGenerator use this implementation for <code>ID</code> attribute generation
 464  
       */
 465  
     public void setIdGenerator(IDGenerator idGenerator) {
 466  0
         this.idGenerator = idGenerator;
 467  0
     }
 468  
     
 469  
     
 470  
     
 471  
     /**
 472  
      * Gets the dynamic configuration setting to be used for bean reading.
 473  
      * @return the BindingConfiguration settings, not null
 474  
      * @since 0.5
 475  
      */
 476  
     public BindingConfiguration getBindingConfiguration() {
 477  67232
         return bindingConfiguration;
 478  
     }
 479  
     
 480  
     /**
 481  
      * Sets the dynamic configuration setting to be used for bean reading.
 482  
      * @param bindingConfiguration the BindingConfiguration settings, not null
 483  
      * @since 0.5
 484  
      */
 485  
     public void setBindingConfiguration(BindingConfiguration bindingConfiguration) {
 486  195
         this.bindingConfiguration = bindingConfiguration;
 487  195
     }
 488  
     
 489  
     /** 
 490  
      * <p>Should generated <code>ID</code> attribute values be added to the elements?</p>
 491  
      * 
 492  
      * <p>If IDs are not being written then if a cycle is encountered in the bean graph, 
 493  
      * then a {@link CyclicReferenceException} will be thrown by the write method.</p>
 494  
      * 
 495  
      * @return true if <code>ID</code> and <code>IDREF</code> attributes are to be written
 496  
      * @deprecated 0.5 use {@link BindingConfiguration#getMapIDs}
 497  
      */
 498  
     public boolean getWriteIDs() {
 499  0
         return getBindingConfiguration().getMapIDs();
 500  
     }
 501  
 
 502  
     /** 
 503  
      * Set whether generated <code>ID</code> attribute values should be added to the elements 
 504  
      * If this property is set to false, then <code>CyclicReferenceException</code> 
 505  
      * will be thrown whenever a cyclic occurs in the bean graph.
 506  
      *
 507  
      * @param writeIDs true if <code>ID</code>'s and <code>IDREF</code>'s should be written
 508  
      * @deprecated 0.5 use {@link BindingConfiguration#setMapIDs}
 509  
      */
 510  
     public void setWriteIDs(boolean writeIDs) {
 511  0
         getBindingConfiguration().setMapIDs( writeIDs );
 512  0
     }
 513  
     
 514  
     /**
 515  
      * <p>Gets whether empty elements should be written into the output.</p>
 516  
      *
 517  
      * <p>An empty element is one that has no attributes, no child elements 
 518  
      * and no body text.
 519  
      * For example, <code>&lt;element/&gt;</code> is an empty element but
 520  
      * <code>&lt;element attr='value'/&gt;</code> is not.</p>
 521  
      *
 522  
      * @return true if empty elements will be written into the output
 523  
      * @since 0.5
 524  
      */
 525  
     public boolean getWriteEmptyElements() {
 526  17542
         return writeEmptyElements;
 527  
     }
 528  
     
 529  
     /**
 530  
      * <p>Sets whether empty elements should be written into the output.</p>
 531  
      *
 532  
      * <p>An empty element is one that has no attributes, no child elements 
 533  
      * and no body text.
 534  
      * For example, <code>&lt;element/&gt;</code> is an empty element but
 535  
      * <code>&lt;element attr='value'/&gt;</code> is not.
 536  
      *
 537  
      * @param writeEmptyElements true if empty elements should be written into the output 
 538  
      * @since 0.5
 539  
      */
 540  
     public void setWriteEmptyElements(boolean writeEmptyElements) {
 541  819
         this.writeEmptyElements = writeEmptyElements;
 542  819
     }
 543  
 
 544  
     /**
 545  
      * <p>Gets the introspector used.</p>
 546  
      *
 547  
      * <p>The {@link XMLBeanInfo} used to map each bean is 
 548  
      * created by the <code>XMLIntrospector</code>.
 549  
      * One way in which the mapping can be customized is 
 550  
      * by altering the <code>XMLIntrospector</code>. </p>
 551  
      *
 552  
      * @return the <code>XMLIntrospector</code> used for introspection
 553  
      */
 554  
     public XMLIntrospector getXMLIntrospector() {
 555  1657
         return introspector;
 556  
     }
 557  
     
 558  
 
 559  
     /**
 560  
      * <p>Sets the introspector to be used.</p>
 561  
      *
 562  
      * <p>The {@link XMLBeanInfo} used to map each bean is 
 563  
      * created by the <code>XMLIntrospector</code>.
 564  
      * One way in which the mapping can be customized is by 
 565  
      * altering the <code>XMLIntrospector</code>. </p>
 566  
      *
 567  
      * @param introspector use this introspector
 568  
      */
 569  
     public void  setXMLIntrospector(XMLIntrospector introspector) {
 570  234
         this.introspector = introspector;
 571  234
     }
 572  
 
 573  
     /**
 574  
      * <p>Gets the current logging implementation.</p>
 575  
      *
 576  
      * @return the <code>Log</code> implementation which this class logs to
 577  
      */ 
 578  
     public final Log getAbstractBeanWriterLog() {
 579  0
         return log;
 580  
     }
 581  
 
 582  
     /**
 583  
      * <p> Set the current logging implementation. </p>
 584  
      *
 585  
      * @param log <code>Log</code> implementation to use
 586  
      */ 
 587  
     public final void setAbstractBeanWriterLog(Log log) {
 588  0
         this.log = log;
 589  0
     }
 590  
         
 591  
     // SAX-style methods
 592  
     //-------------------------------------------------------------------------    
 593  
         
 594  
     /**
 595  
      * Writes the start tag for an element.
 596  
      *
 597  
      * @param uri the element's namespace uri
 598  
      * @param localName the element's local name 
 599  
      * @param qName the element's qualified name
 600  
      * @param attr the element's attributes
 601  
      *
 602  
      * @throws IOException if an IO problem occurs during writing
 603  
      * @throws SAXException if an SAX problem occurs during writing 
 604  
      * @since 0.5
 605  
      */
 606  
     protected void startElement(
 607  
                                 WriteContext context,
 608  
                                 String uri, 
 609  
                                 String localName, 
 610  
                                 String qName, 
 611  
                                 Attributes attr)
 612  
                                     throws
 613  
                                         IOException,
 614  
                                         SAXException {
 615  
         // for backwards compatbility call older methods
 616  0
         startElement(uri, localName, qName, attr);                                    
 617  0
     }
 618  
     
 619  
     /**
 620  
      * Writes the end tag for an element
 621  
      *
 622  
      * @param uri the element's namespace uri
 623  
      * @param localName the element's local name 
 624  
      * @param qName the element's qualified name
 625  
      *
 626  
      * @throws IOException if an IO problem occurs during writing
 627  
      * @throws SAXException if an SAX problem occurs during writing 
 628  
      * @since 0.5
 629  
      */
 630  
     protected void endElement(
 631  
                                 WriteContext context,
 632  
                                 String uri, 
 633  
                                 String localName, 
 634  
                                 String qName)
 635  
                                     throws
 636  
                                         IOException,
 637  
                                         SAXException {
 638  
         // for backwards compatibility call older interface
 639  0
         endElement(uri, localName, qName);                                    
 640  0
     }
 641  
     
 642  
     /** 
 643  
      * Writes body text
 644  
      *
 645  
      * @param text the body text to be written
 646  
      *
 647  
      * @throws IOException if an IO problem occurs during writing
 648  
      * @throws SAXException if an SAX problem occurs during writing 
 649  
      * @since 0.5
 650  
      */
 651  
     protected void bodyText(WriteContext context, String text) 
 652  
                                 throws IOException, SAXException {
 653  
         // for backwards compatibility call older interface
 654  0
         bodyText(text);                            
 655  0
     }
 656  
         
 657  
     // Older SAX-style methods
 658  
     //-------------------------------------------------------------------------    
 659  
         
 660  
     /**
 661  
      * Writes the start tag for an element.
 662  
      *
 663  
      * @param uri the element's namespace uri
 664  
      * @param localName the element's local name 
 665  
      * @param qName the element's qualified name
 666  
      * @param attr the element's attributes
 667  
      *
 668  
      * @throws IOException if an IO problem occurs during writing
 669  
      * @throws SAXException if an SAX problem occurs during writing 
 670  
      * @deprecated 0.5 use {@link #startElement(WriteContext, String, String, String, Attributes)}
 671  
      */
 672  
     protected void startElement(
 673  
                                 String uri, 
 674  
                                 String localName, 
 675  
                                 String qName, 
 676  
                                 Attributes attr)
 677  
                                     throws
 678  
                                         IOException,
 679  0
                                         SAXException {}
 680  
     
 681  
     /**
 682  
      * Writes the end tag for an element
 683  
      *
 684  
      * @param uri the element's namespace uri
 685  
      * @param localName the element's local name 
 686  
      * @param qName the element's qualified name
 687  
      *
 688  
      * @throws IOException if an IO problem occurs during writing
 689  
      * @throws SAXException if an SAX problem occurs during writing 
 690  
      * @deprecated 0.5 use {@link #endElement(WriteContext, String, String, String)}
 691  
      */
 692  
     protected void endElement(
 693  
                                 String uri, 
 694  
                                 String localName, 
 695  
                                 String qName)
 696  
                                     throws
 697  
                                         IOException,
 698  0
                                         SAXException {}
 699  
     
 700  
     /** 
 701  
      * Writes body text
 702  
      *
 703  
      * @param text the body text to be written
 704  
      *
 705  
      * @throws IOException if an IO problem occurs during writing
 706  
      * @throws SAXException if an SAX problem occurs during writing 
 707  
      * @deprecated 0.5 use {@link #bodyText(WriteContext, String)}
 708  
      */
 709  0
     protected void bodyText(String text) throws IOException, SAXException {}
 710  
     
 711  
     // Implementation methods
 712  
     //-------------------------------------------------------------------------    
 713  
 
 714  
     /** 
 715  
      * Writes the given element 
 716  
      *
 717  
      * @param namespaceUri the namespace uri
 718  
      * @param localName the local name
 719  
      * @param qualifiedName qualified name to use for the element
 720  
      * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
 721  
      * @param context the <code>Context</code> to use to evaluate the bean expressions
 722  
      * @throws IOException if an IO problem occurs during writing
 723  
      * @throws SAXException if an SAX problem occurs during writing 
 724  
      * @throws IntrospectionException if a java beans introspection problem occurs
 725  
      */
 726  
     private void writeElement(
 727  
                             String namespaceUri,
 728  
                             String localName,
 729  
                             String qualifiedName, 
 730  
                             ElementDescriptor elementDescriptor, 
 731  
                             Context context ) 
 732  
                                 throws 
 733  
                                     IOException, 
 734  
                                     SAXException,
 735  
                                     IntrospectionException {
 736  16502
         if( log.isTraceEnabled() ) {
 737  0
             log.trace( "Writing: " + qualifiedName + " element: " + elementDescriptor );
 738  
         }
 739  
                 
 740  16502
         if ( !ignoreElement( elementDescriptor, namespaceUri, localName, qualifiedName, context )) {
 741  16346
             if ( log.isTraceEnabled() ) {
 742  0
                 log.trace( "Element " + elementDescriptor + " is empty." );
 743  
             }
 744  
             
 745  32692
             Attributes attributes = addNamespaceDeclarations(
 746  16346
                 new ElementAttributes( elementDescriptor, context ), namespaceUri);
 747  16346
             writeContext.setCurrentDescriptor(elementDescriptor);
 748  32692
             startElement( 
 749  16346
                             writeContext,
 750  16346
                             namespaceUri, 
 751  16346
                             localName, 
 752  16346
                             qualifiedName,
 753  16346
                             attributes);
 754  
            
 755  16346
             writeElementContent( elementDescriptor, context ) ;
 756  16164
             writeContext.setCurrentDescriptor(elementDescriptor);
 757  16164
             endElement( writeContext, namespaceUri, localName, qualifiedName );
 758  
         }
 759  16320
     }
 760  
     
 761  
     /**
 762  
      * Adds namespace declarations (if any are needed) to the given attributes.
 763  
      * @param attributes Attributes, not null
 764  
      * @param elementNamespaceUri the URI for the enclosing element, possibly null
 765  
      * @return Attributes, not null
 766  
      */
 767  
     private Attributes addNamespaceDeclarations(Attributes attributes, String elementNamespaceUri) {
 768  17464
         Attributes result = attributes;
 769  17464
         AttributesImpl withDeclarations = null; 
 770  43118
         for (int i=-1, size=attributes.getLength(); i<size ; i++) {
 771  25654
             String uri = null;
 772  25654
             if (i == -1) {
 773  17464
                 uri = elementNamespaceUri;
 774  
             } else {
 775  8190
                 uri = attributes.getURI(i);
 776  
             }
 777  25654
             if (uri != null && !"".equals(uri) && !namespacesDeclared.contains(uri)) {
 778  208
                 if (withDeclarations == null) {
 779  208
                     withDeclarations = new AttributesImpl(attributes);
 780  
                 }
 781  416
                 withDeclarations.addAttribute("", "", "xmlns:" 
 782  208
                     + getXMLIntrospector().getConfiguration().getPrefixMapper().getPrefix(uri), "NOTATION", uri);
 783  208
                 namespacesDeclared.add(uri);
 784  
             }
 785  
         }
 786  
         
 787  17464
         if (withDeclarations != null) {
 788  208
             result = withDeclarations;
 789  
         }
 790  17464
         return result;
 791  
     }
 792  
     
 793  
     
 794  
     /** 
 795  
      * Writes the given element adding an ID attribute 
 796  
      *
 797  
      * @param namespaceUri the namespace uri
 798  
      * @param localName the local name
 799  
      * @param qualifiedName the qualified name
 800  
      * @param elementDescriptor the ElementDescriptor describing this element
 801  
      * @param context the context being evaliated against
 802  
      * @param idAttribute the qualified name of the <code>ID</code> attribute 
 803  
      * @param idValue the value for the <code>ID</code> attribute 
 804  
      * @throws IOException if an IO problem occurs during writing
 805  
      * @throws SAXException if an SAX problem occurs during writing 
 806  
      * @throws IntrospectionException if a java beans introspection problem occurs
 807  
      */
 808  
     private void writeElement( 
 809  
                             String namespaceUri,
 810  
                             String localName,
 811  
                             String qualifiedName, 
 812  
                             ElementDescriptor elementDescriptor, 
 813  
                             Context context,
 814  
                             String idAttribute,
 815  
                             String idValue ) 
 816  
                                 throws 
 817  
                                     IOException, 
 818  
                                     SAXException,
 819  
                                     IntrospectionException {
 820  
                    
 821  1053
         if ( !ignoreElement( elementDescriptor, namespaceUri, localName, qualifiedName, context ) ) {
 822  1053
             writeContext.setCurrentDescriptor(elementDescriptor);
 823  2106
             Attributes attributes = new IDElementAttributes( 
 824  1053
                         elementDescriptor, 
 825  1053
                         context, 
 826  1053
                         idAttribute, 
 827  1053
                         idValue );
 828  2106
             startElement( 
 829  1053
                         writeContext,
 830  1053
                         namespaceUri, 
 831  1053
                         localName, 
 832  1053
                         qualifiedName,
 833  1053
                         addNamespaceDeclarations(attributes, namespaceUri));
 834  
     
 835  1053
             writeElementContent( elementDescriptor, context ) ;
 836  1053
             writeContext.setCurrentDescriptor(elementDescriptor);
 837  1053
             endElement( writeContext, namespaceUri, localName, qualifiedName );
 838  0
         } else if ( log.isTraceEnabled() ) {
 839  0
             log.trace( "Element " + qualifiedName + " is empty." );
 840  
         }
 841  1053
     }
 842  
     
 843  
 
 844  
     /**
 845  
      * Write attributes, child elements and element end 
 846  
      * 
 847  
      * @param uri the element namespace uri 
 848  
      * @param localName the local name of the element
 849  
      * @param qualifiedName the qualified name of the element
 850  
      * @param elementDescriptor the descriptor for this element
 851  
      * @param context evaluate against this context
 852  
      * @throws IOException if an IO problem occurs during writing
 853  
      * @throws SAXException if an SAX problem occurs during writing 
 854  
      * @throws IntrospectionException if a java beans introspection problem occurs
 855  
      */
 856  
     private void writeRestOfElement( 
 857  
                             String uri,
 858  
                             String localName,
 859  
                             String qualifiedName, 
 860  
                             ElementDescriptor elementDescriptor, 
 861  
                             Context context ) 
 862  
                                 throws 
 863  
                                     IOException, 
 864  
                                     SAXException,
 865  
                                     IntrospectionException {
 866  
 
 867  0
         writeElementContent( elementDescriptor, context );
 868  0
     }
 869  
 
 870  
     /**
 871  
      * Writes an element with a <code>IDREF</code> attribute 
 872  
      *
 873  
      * @param uri the namespace uri
 874  
      * @param localName the local name
 875  
      * @param qualifiedName of the element with <code>IDREF</code> attribute 
 876  
      * @param idrefAttributeName the qualified name of the <code>IDREF</code> attribute 
 877  
      * @param idrefAttributeValue the value for the <code>IDREF</code> attribute 
 878  
      * @throws IOException if an IO problem occurs during writing
 879  
      * @throws SAXException if an SAX problem occurs during writing 
 880  
      * @throws IntrospectionException if a java beans introspection problem occurs
 881  
      */
 882  
     private void writeIDREFElement( 
 883  
                                     ElementDescriptor elementDescriptor,
 884  
                                     String uri,
 885  
                                     String localName,
 886  
                                     String qualifiedName, 
 887  
                                     String idrefAttributeName,
 888  
                                     String idrefAttributeValue ) 
 889  
                                         throws 
 890  
                                             IOException, 
 891  
                                             SAXException,
 892  
                                             IntrospectionException {
 893  
 
 894  
         
 895  
         
 896  
         // write IDREF element
 897  65
         AttributesImpl attributes = new AttributesImpl();
 898  
         // XXX for the moment, assign IDREF to default namespace
 899  130
         attributes.addAttribute( 
 900  65
                                 "",
 901  65
                                 idrefAttributeName, 
 902  65
                                 idrefAttributeName,
 903  65
                                 "IDREF",    
 904  65
                                 idrefAttributeValue);
 905  65
         writeContext.setCurrentDescriptor(elementDescriptor);
 906  65
         startElement( writeContext, uri, localName, qualifiedName, addNamespaceDeclarations(attributes, uri));        
 907  65
         endElement( writeContext, uri, localName, qualifiedName );
 908  65
     }
 909  
     
 910  
     /** 
 911  
      * Writes the element content.
 912  
      *
 913  
      * @param elementDescriptor the <code>ElementDescriptor</code> to write as xml 
 914  
      * @param context the <code>Context</code> to use to evaluate the bean expressions
 915  
      * 
 916  
      * @throws IOException if an IO problem occurs during writing
 917  
      * @throws SAXException if an SAX problem occurs during writing 
 918  
      * @throws IntrospectionException if a java beans introspection problem occurs
 919  
      */
 920  
     private void writeElementContent( 
 921  
                         ElementDescriptor elementDescriptor, 
 922  
                         Context context ) 
 923  
                             throws 
 924  
                                 IOException, 
 925  
                                 SAXException,
 926  
                                 IntrospectionException {     
 927  17399
         writeContext.setCurrentDescriptor( elementDescriptor );              
 928  17399
         Descriptor[] childDescriptors = elementDescriptor.getContentDescriptors();
 929  17399
         if ( childDescriptors != null && childDescriptors.length > 0 ) {
 930  
             // process child elements
 931  20695
             for ( int i = 0, size = childDescriptors.length; i < size; i++ ) {
 932  13773
                 if (childDescriptors[i] instanceof ElementDescriptor) {
 933  
                     // Element content
 934  13721
                     ElementDescriptor childDescriptor = (ElementDescriptor) childDescriptors[i];
 935  13721
                     Context childContext = context;
 936  13721
                     childContext.pushOptions(childDescriptor.getOptions());
 937  13721
                     Expression childExpression = childDescriptor.getContextExpression();
 938  13721
                     if ( childExpression != null ) {
 939  5375
                         Object childBean = childExpression.evaluate( context );
 940  5375
                         if ( childBean != null ) {
 941  5232
                             String qualifiedName = childDescriptor.getQualifiedName();
 942  5232
                             String namespaceUri = childDescriptor.getURI();
 943  5232
                             String localName = childDescriptor.getLocalName();
 944  
                             // XXXX: should we handle nulls better
 945  5232
                             if ( childBean instanceof Iterator ) {
 946  12374
                                 for ( Iterator iter = (Iterator) childBean; iter.hasNext(); ) {
 947  5498
                                     Object object = iter.next();
 948  5498
                                     if (object == null) {
 949  0
                                         continue;
 950  
                                     }
 951  10996
                                     writeBean( 
 952  5498
                                             namespaceUri, 
 953  5498
                                             localName, 
 954  5498
                                             qualifiedName, 
 955  5498
                                             object, 
 956  5498
                                             childDescriptor,
 957  5498
                                             context );
 958  
                                 }
 959  
                             } else {
 960  3536
                                 writeBean( 
 961  1768
                                             namespaceUri, 
 962  1768
                                             localName, 
 963  1768
                                             qualifiedName, 
 964  1768
                                             childBean, 
 965  1768
                                             childDescriptor,
 966  1768
                                             context );
 967  
                             }
 968  
                         }                    
 969  
                     } else {
 970  16692
                         writeElement(
 971  8346
                                     childDescriptor.getURI(), 
 972  8346
                                     childDescriptor.getLocalName(), 
 973  8346
                                     childDescriptor.getQualifiedName(), 
 974  8346
                                     childDescriptor, 
 975  8346
                                     childContext );
 976  
                     }
 977  13539
                     childContext.popOptions();
 978  
                 } else {
 979  
                     // Mixed text content
 980  
                     // evaluate the body text 
 981  52
                     Expression expression = childDescriptors[i].getTextExpression();
 982  52
                     if ( expression != null ) {
 983  52
                         Object value = expression.evaluate( context );
 984  104
                         String text = convertToString( 
 985  52
                                                         value, 
 986  52
                                                         childDescriptors[i], 
 987  52
                                                         context );
 988  52
                         if ( text != null && text.length() > 0 ) {;
 989  52
                             bodyText( writeContext, text );
 990  
                         }               
 991  
                     }
 992  
                 }
 993  
             }
 994  
         } else {
 995  
             // evaluate the body text 
 996  10295
             Expression expression = elementDescriptor.getTextExpression();
 997  10295
             if ( expression != null ) {
 998  7709
                 Object value = expression.evaluate( context );
 999  7709
                 String text = convertToString( value, elementDescriptor, context );
 1000  7709
                 if ( text != null && text.length() > 0 ) {
 1001  7033
                     bodyText( writeContext, text );
 1002  
                 }
 1003  
             }
 1004  
         }
 1005  17217
     }
 1006  
 
 1007  
     /**
 1008  
      * Pushes the bean onto the ancestry stack.
 1009  
      * If IDs are not being written, then check for cyclic references.
 1010  
      *
 1011  
      * @param bean push this bean onto the ancester stack
 1012  
      */
 1013  
     protected void pushBean( Object bean ) {
 1014  
         // check that we don't have a cyclic reference when we're not writing IDs
 1015  7897
         if ( !getBindingConfiguration().getMapIDs() ) {
 1016  6766
             Iterator it = beanStack.iterator();
 1017  22320
             while ( it.hasNext() ) {
 1018  8814
                 Object next = it.next();
 1019  
                 // use absolute equality rather than equals
 1020  
                 // we're only really bothered if objects are actually the same
 1021  8814
                 if ( bean == next ) {
 1022  26
                     if ( log.isDebugEnabled() ) {
 1023  0
                         log.debug("Element stack: ");
 1024  0
                         Iterator debugStack = beanStack.iterator();
 1025  0
                         while ( debugStack.hasNext() ) {
 1026  0
                             log.debug(debugStack.next());
 1027  
                         }
 1028  
                     }
 1029  26
                     log.error("Cyclic reference at bean: " + bean);
 1030  26
                     throw new CyclicReferenceException();
 1031  
                 }
 1032  
             }
 1033  
         }
 1034  7871
         if (log.isTraceEnabled()) {
 1035  0
             log.trace( "Pushing onto object stack: " + bean );
 1036  
         }
 1037  7871
         beanStack.push( bean );
 1038  7871
     }
 1039  
     
 1040  
     /** 
 1041  
      * Pops the top bean off from the ancestry stack 
 1042  
      *
 1043  
      * @return the last object pushed onto the ancester stack
 1044  
      */
 1045  
     protected Object popBean() {
 1046  7741
         Object bean = beanStack.pop();
 1047  7741
         if (log.isTraceEnabled()) {
 1048  0
             log.trace( "Popped from object stack: " + bean );
 1049  
         }
 1050  7741
         return bean;
 1051  
     }
 1052  
     
 1053  
     /** 
 1054  
      * Should this element (and children) be written out?
 1055  
      *
 1056  
      * @param descriptor the <code>ElementDescriptor</code> to evaluate
 1057  
      * @param context the <code>Context</code> against which the element will be evaluated
 1058  
      * @return true if this element should be written out
 1059  
      * @throws IntrospectionException
 1060  
      */
 1061  
     private boolean ignoreElement( ElementDescriptor descriptor, String namespaceUri, String localName, String qualifiedName, Context context ) throws IntrospectionException {        
 1062  17620
         if (getBindingConfiguration().getValueSuppressionStrategy().suppressElement(descriptor, namespaceUri, localName, qualifiedName, context.getBean())) {
 1063  78
             return true;
 1064  
         }
 1065  
             
 1066  17542
         if ( ! getWriteEmptyElements() ) {
 1067  520
             return isEmptyElement( descriptor, context );
 1068  
         }
 1069  17022
         return false;
 1070  
     }
 1071  
     
 1072  
     /** 
 1073  
      * <p>Will evaluating this element against this context result in an empty element?</p>
 1074  
      *
 1075  
      * <p>An empty element is one that has no attributes, no child elements 
 1076  
      * and no body text.
 1077  
      * For example, <code>&lt;element/&gt;</code> is an empty element but
 1078  
      * <code>&lt;element attr='value'/&gt;</code> is not.</p>
 1079  
      * 
 1080  
      * @param descriptor the <code>ElementDescriptor</code> to evaluate
 1081  
      * @param context the <code>Context</code> against which the element will be evaluated
 1082  
      * @return true if this element is empty on evaluation
 1083  
      * @throws IntrospectionException
 1084  
      */
 1085  
     private boolean isEmptyElement( ElementDescriptor descriptor, Context context ) throws IntrospectionException {
 1086  
         //TODO: this design isn't too good
 1087  
         // to would be much better to render just once 
 1088  1014
         if ( log.isTraceEnabled() ) {
 1089  0
             log.trace( "Is " + descriptor + " empty?" );
 1090  
         }
 1091  
                 
 1092  
         // an element which has attributes is not empty
 1093  1014
         if ( descriptor.hasAttributes() ) {
 1094  0
             log.trace( "Element has attributes." );
 1095  0
             return false;
 1096  
         }
 1097  
         
 1098  
         // an element is not empty if it has a non-empty body
 1099  1014
         Expression expression = descriptor.getTextExpression();
 1100  1014
         if ( expression != null ) {
 1101  533
             Object value = expression.evaluate( context );
 1102  533
             String text = convertToString( value, descriptor, context );
 1103  533
             if ( text != null && text.length() > 0 ) {
 1104  403
                 log.trace( "Element has body text which isn't empty." );
 1105  403
                 return false;
 1106  
             }
 1107  
         }
 1108  
         
 1109  
         // always write out loops - even when they have no elements
 1110  611
         if ( descriptor.isCollective() ) {
 1111  39
             log.trace("Loop type so not empty.");
 1112  39
             return false;
 1113  
         }
 1114  
         
 1115  
         // now test child elements
 1116  
         // an element is empty if it has no non-empty child elements
 1117  572
         if ( descriptor.hasChildren() ) {
 1118  481
             for ( int i=0, size=descriptor.getElementDescriptors().length; i<size; i++ ) {
 1119  403
                 if ( ! isEmptyElement( descriptor.getElementDescriptors()[i], context ) ) {
 1120  247
                     log.trace( "Element has child which isn't empty." );
 1121  247
                     return false;
 1122  
                 }
 1123  
             }
 1124  
         }
 1125  
         
 1126  325
         if ( descriptor.isHollow() )
 1127  
         {
 1128  117
             Expression contentExpression = descriptor.getContextExpression();
 1129  117
             if (contentExpression != null) {
 1130  117
                 Object childBean = contentExpression.evaluate(context);
 1131  117
                 if (childBean != null)
 1132  
                 {
 1133  91
                     XMLBeanInfo xmlBeanInfo = findXMLBeanInfo(childBean, descriptor);
 1134  91
                     Object currentBean = context.getBean();
 1135  91
                     context.setBean(childBean);
 1136  91
                     boolean result = isEmptyElement(xmlBeanInfo.getElementDescriptor(), context);
 1137  91
                     context.setBean(currentBean);
 1138  91
                     return result;
 1139  
                 }
 1140  
             }
 1141  
         }
 1142  
         
 1143  234
         log.trace( "Element is empty." );
 1144  234
         return true;
 1145  
     }
 1146  
     
 1147  
     
 1148  
     /**
 1149  
      * Attributes backed by attribute descriptors.
 1150  
      * ID/IDREFs not set.
 1151  
      */
 1152  
     private class ElementAttributes implements Attributes {
 1153  
         /** Attribute descriptors backing the <code>Attributes</code> */
 1154  
         private AttributeDescriptor[] attributes;
 1155  
         /** Context to be evaluated when finding values */
 1156  
         private Context context;
 1157  
         /** Cached attribute values */
 1158  
         private String[] values;
 1159  
         /** The number of unsuppressed attributes */
 1160  
         private int length;
 1161  
         
 1162  
         
 1163  
         /** 
 1164  
          * Construct attributes for element and context.
 1165  
          *
 1166  
          * @param descriptor the <code>ElementDescriptor</code> describing the element
 1167  
          * @param context evaluate against this context
 1168  
          */
 1169  17399
         ElementAttributes( ElementDescriptor descriptor, Context context ) {
 1170  17399
             this.context = context;
 1171  17399
             init(descriptor.getAttributeDescriptors());
 1172  17399
         }
 1173  
         
 1174  
         private void init(AttributeDescriptor[] baseAttributes) {
 1175  17399
             attributes = new AttributeDescriptor[baseAttributes.length];
 1176  17399
             values = new String[baseAttributes.length];
 1177  17399
             int index = 0;
 1178  24692
             for (int i=0, size=baseAttributes.length; i<size; i++) {
 1179  7293
                 AttributeDescriptor baseAttribute = baseAttributes[i];
 1180  7293
                 String attributeValue = valueAttribute(baseAttribute);
 1181  7293
                 if (attributeValue != null 
 1182  7293
                         && !context.getValueSuppressionStrategy()
 1183  7293
                                         .suppressAttribute(baseAttribute, attributeValue)) {
 1184  7072
                     values[index] = attributeValue;
 1185  7072
                     attributes[index] = baseAttribute;
 1186  7072
                     index++;
 1187  
                 }
 1188  
             }
 1189  17399
             length = index;
 1190  17399
         }
 1191  
         
 1192  
         private String valueAttribute(AttributeDescriptor attribute) {
 1193  7293
             Expression expression = attribute.getTextExpression();
 1194  7293
             if ( expression != null ) {
 1195  7293
                 Object value = expression.evaluate( context );
 1196  7293
                 return convertToString( value, attribute, context );
 1197  
             }
 1198  
             
 1199  0
             return "";
 1200  
         }
 1201  
         
 1202  
         /**
 1203  
          * Gets the index of an attribute by qualified name.
 1204  
          * 
 1205  
          * @param qName the qualified name of the attribute
 1206  
          * @return the index of the attribute - or -1 if there is no matching attribute
 1207  
          */
 1208  
         public int getIndex( String qName ) {
 1209  0
             for ( int i=0; i<attributes.length; i++ ) {
 1210  0
                 if (attributes[i].getQualifiedName() != null 
 1211  0
                        && attributes[i].getQualifiedName().equals( qName )) {
 1212  0
                     return i;
 1213  
                 }
 1214  
             }
 1215  0
             return -1;
 1216  
         }
 1217  
         
 1218  
         /**
 1219  
          * Gets the index of an attribute by namespace name.
 1220  
          *
 1221  
          * @param uri the namespace uri of the attribute
 1222  
          * @param localName the local name of the attribute
 1223  
          * @return the index of the attribute - or -1 if there is no matching attribute
 1224  
          */
 1225  
         public int getIndex( String uri, String localName ) {
 1226  0
             for ( int i=0; i<attributes.length; i++ ) {
 1227  
                 if (
 1228  0
                         attributes[i].getURI() != null 
 1229  0
                         && attributes[i].getURI().equals(uri)
 1230  0
                         && attributes[i].getLocalName() != null 
 1231  0
                         && attributes[i].getURI().equals(localName)) {
 1232  0
                     return i;
 1233  
                 }
 1234  
             } 
 1235  
             
 1236  0
             return -1;
 1237  
         }
 1238  
         
 1239  
         /**
 1240  
          * Gets the number of attributes in the list.
 1241  
          *
 1242  
          * @return the number of attributes in this list
 1243  
          */
 1244  
         public int getLength() {
 1245  61110
             return length;
 1246  
         }
 1247  
         
 1248  
         /** 
 1249  
          * Gets the local name by index.
 1250  
          * 
 1251  
          * @param index the attribute index (zero based)
 1252  
          * @return the attribute local name - or null if the index is out of range
 1253  
          */
 1254  
         public String getLocalName( int index ) {
 1255  104
             if ( indexInRange( index ) ) {
 1256  104
                 return attributes[index].getLocalName();
 1257  
             }
 1258  
             
 1259  0
             return null;
 1260  
         }
 1261  
         
 1262  
         /**
 1263  
          * Gets the qualified name by index.
 1264  
          *
 1265  
          * @param index the attribute index (zero based)
 1266  
          * @return the qualified name of the element - or null if the index is our of range
 1267  
          */
 1268  
         public String getQName( int index ) {
 1269  6994
             if ( indexInRange( index ) ) {
 1270  6994
                 return attributes[index].getQualifiedName();
 1271  
             }
 1272  
             
 1273  0
             return null;
 1274  
         }
 1275  
         
 1276  
         /**
 1277  
          * Gets the attribute SAX type by namespace name.
 1278  
          *
 1279  
          * @param index the attribute index (zero based)
 1280  
          * @return the attribute type (as a string) or null if the index is out of range
 1281  
          */
 1282  
         public String getType( int index ) {
 1283  78
             if ( indexInRange( index ) ) {
 1284  78
                 return "CDATA";
 1285  
             }
 1286  0
             return null;
 1287  
         }
 1288  
         
 1289  
         /**
 1290  
          * Gets the attribute SAX type by qualified name.
 1291  
          *
 1292  
          * @param qName the qualified name of the attribute
 1293  
          * @return the attribute type (as a string) or null if the attribute is not in the list
 1294  
          */
 1295  
         public String getType( String qName ) {
 1296  0
             return getType( getIndex( qName ) );
 1297  
         }
 1298  
         
 1299  
         /**
 1300  
          * Gets the attribute SAX type by namespace name.
 1301  
          *
 1302  
          * @param uri the namespace uri of the attribute
 1303  
          * @param localName the local name of the attribute
 1304  
          * @return the attribute type (as a string) or null if the attribute is not in the list
 1305  
          */
 1306  
         public String getType( String uri, String localName ) {
 1307  0
             return getType( getIndex( uri, localName ));
 1308  
         }
 1309  
         
 1310  
         /**
 1311  
          * Gets the namespace URI for attribute at the given index.
 1312  
          *
 1313  
          * @param index the attribute index (zero-based)
 1314  
          * @return the namespace URI (empty string if none is available) 
 1315  
          * or null if the index is out of range
 1316  
          */
 1317  
         public String getURI( int index ) {
 1318  7124
             if ( indexInRange( index ) ) {
 1319  7124
                 return attributes[index].getURI();
 1320  
             }
 1321  0
             return null;
 1322  
         }
 1323  
         
 1324  
         /**
 1325  
          * Gets the value for the attribute at given index.
 1326  
          * 
 1327  
          * @param index the attribute index (zero based)
 1328  
          * @return the attribute value or null if the index is out of range
 1329  
          * @todo add value caching
 1330  
          */
 1331  
         public String getValue( int index ) {
 1332  6968
             if ( indexInRange( index ) ) {
 1333  6968
                 return values[index];
 1334  
             }
 1335  0
             return null;
 1336  
         }
 1337  
         
 1338  
         /**
 1339  
          * Gets the value for the attribute by qualified name.
 1340  
          * 
 1341  
          * @param qName the qualified name 
 1342  
          * @return the attribute value or null if there are no attributes 
 1343  
          * with the given qualified name
 1344  
          * @todo add value caching
 1345  
          */
 1346  
         public String getValue( String qName ) {
 1347  0
             return getValue( getIndex( qName ) );
 1348  
         }
 1349  
         
 1350  
         /**
 1351  
          * Gets the value for the attribute by namespace name.
 1352  
          * 
 1353  
          * @param uri the namespace URI of the attribute
 1354  
          * @param localName the local name of the attribute
 1355  
          * @return the attribute value or null if there are not attributes 
 1356  
          * with the given namespace and local name
 1357  
          * @todo add value caching
 1358  
          */
 1359  
         public String getValue( String uri, String localName ) {
 1360  0
             return getValue( getIndex( uri, localName ) );
 1361  
         }
 1362  
         
 1363  
         /**
 1364  
          * Is the given index within the range of the attribute list
 1365  
          *
 1366  
          * @param index the index whose range will be checked
 1367  
          * @return true if the index with within the range of the attribute list
 1368  
          */
 1369  
         private boolean indexInRange( int index ) {
 1370  21268
             return ( index >= 0 && index < getLength() );
 1371  
         }
 1372  
     }
 1373  
     
 1374  
     /**
 1375  
      * Attributes with generate ID/IDREF attributes
 1376  
      * //TODO: refactor the ID/REF generation so that it's fixed at introspection
 1377  
      * and the generators are placed into the Context.
 1378  
      * @author <a href='http://jakarta.apache.org/'>Apache Commons Team</a>
 1379  
      * @version $Revision: 345774 $
 1380  
      */
 1381  
     private class IDElementAttributes extends ElementAttributes {
 1382  
                 /** ID attribute value */
 1383  
                 private String idValue;
 1384  
                 /** ID attribute name */
 1385  
                 private String idAttributeName;
 1386  
 
 1387  1053
                 private boolean matchingAttribute = false;
 1388  
                 private int length;
 1389  
                 private int idIndex;
 1390  
                 
 1391  
                 /** 
 1392  
                  * Construct attributes for element and context.
 1393  
                  *
 1394  
                  * @param descriptor the <code>ElementDescriptor</code> describing the element
 1395  
                  * @param context evaluate against this context
 1396  
                  * @param idAttributeName the name of the id attribute 
 1397  
                  * @param idValue the ID attribute value
 1398  
                  */
 1399  
                 IDElementAttributes( 
 1400  
                                                         ElementDescriptor descriptor, 
 1401  
                                                         Context context, 
 1402  
                                                         String idAttributeName,
 1403  
                                                         String idValue) {
 1404  1053
                         super(descriptor, context);
 1405  1053
                         this.idValue = idValue;
 1406  1053
                         this.idAttributeName = idAttributeName;
 1407  
                         
 1408  
                         // see if we have already have a matching attribute descriptor
 1409  1053
                         AttributeDescriptor[] attributeDescriptors = descriptor.getAttributeDescriptors();
 1410  1053
                         length = super.getLength();
 1411  1287
                         for (int i=0; i<length; i++) {
 1412  234
                                 if (idAttributeName.equals(attributeDescriptors[i].getQualifiedName())) {
 1413  0
                                         matchingAttribute = true;
 1414  0
                                         idIndex = i;
 1415  0
                                         break;
 1416  
                                 }
 1417  
                         }
 1418  1053
                         if (!matchingAttribute) {
 1419  1053
                                 length += 1;
 1420  1053
                                 idIndex = length-1;
 1421  
                         }
 1422  1053
                 }            
 1423  
                 
 1424  
         public int getIndex(String uri, String localName) {
 1425  0
             if (localName.equals(idAttributeName)) {
 1426  0
                     return idIndex;
 1427  
             }
 1428  
                 
 1429  0
             return super.getIndex(uri, localName);
 1430  
         }
 1431  
 
 1432  
         public int getIndex(String qName) {
 1433  0
                         if (qName.equals(idAttributeName)) {
 1434  0
                                 return idIndex;
 1435  
                         }
 1436  
                         
 1437  0
             return super.getIndex(qName);
 1438  
         }
 1439  
 
 1440  
         public int getLength() {
 1441  3692
             return length;
 1442  
         }
 1443  
 
 1444  
         public String getLocalName(int index) {
 1445  91
             if (index == idIndex) {
 1446  39
                     return idAttributeName;
 1447  
             }
 1448  52
             return super.getLocalName(index);
 1449  
         }
 1450  
 
 1451  
         public String getQName(int index) {
 1452  1131
                         if (index == idIndex) {
 1453  975
                                 return idAttributeName;
 1454  
                         }
 1455  156
             return super.getQName(index);
 1456  
         }
 1457  
 
 1458  
         public String getType(int index) {
 1459  39
                         if (index == idIndex) {
 1460  13
                                 return "ID";
 1461  
                         }
 1462  26
             return super.getType(index);
 1463  
         }
 1464  
 
 1465  
         public String getType(String uri, String localName) {
 1466  0
             return getType(getIndex(uri, localName));
 1467  
         }
 1468  
 
 1469  
         public String getType(String qName) {
 1470  0
             return getType(getIndex(qName));
 1471  
         }
 1472  
 
 1473  
         public String getURI(int index) {
 1474  
                 //TODO: this is probably wrong
 1475  
                 // probably need to move ID management into introspection
 1476  
                 // before we can handle this namespace bit correctly
 1477  1287
                         if (index == idIndex) {
 1478  1053
                                 return "";
 1479  
                         }
 1480  234
             return super.getURI(index);
 1481  
         }
 1482  
 
 1483  
         public String getValue(int index) {
 1484  1079
             if (index == idIndex) {
 1485  949
                     return idValue;
 1486  
             }
 1487  130
             return super.getValue(index);
 1488  
         }
 1489  
 
 1490  
         public String getValue(String uri, String localName) {
 1491  0
             return getValue(getIndex(uri, localName));
 1492  
         }
 1493  
 
 1494  
         public String getValue(String qName) {
 1495  0
             return getValue(getIndex(qName));
 1496  
         }
 1497  
 
 1498  
     }
 1499  
     
 1500  
     
 1501  
     // OLD API (DEPRECATED)
 1502  
     // --------------------------------------------------------------------------------------
 1503  
     
 1504  
     
 1505  
     /** 
 1506  
      * Get the indentation for the current element. 
 1507  
      * Used for pretty priting.
 1508  
      *
 1509  
      * @return the amount that the current element is indented
 1510  
      * @deprecated 0.5 replaced by new SAX inspired API
 1511  
      */
 1512  
     protected int getIndentLevel() {
 1513  0
         return 0;
 1514  
     }
 1515  
     
 1516  
     // Expression methods
 1517  
     //-------------------------------------------------------------------------    
 1518  
 
 1519  
     /** 
 1520  
      * Express an element tag start using given qualified name.
 1521  
      *
 1522  
      * @param qualifiedName the qualified name of the element to be expressed
 1523  
      * @throws IOException if an IO problem occurs during writing
 1524  
      * @throws SAXException if an SAX problem occurs during writing 
 1525  
      * @deprecated 0.5 replaced by new SAX inspired API
 1526  
      */
 1527  
     protected void expressElementStart(String qualifiedName) 
 1528  
                                         throws IOException, SAXException {
 1529  
         // do nothing
 1530  0
     }
 1531  
                                         
 1532  
     /** 
 1533  
      * Express an element tag start using given qualified name.
 1534  
      *
 1535  
      * @param uri the namespace uri 
 1536  
      * @param localName the local name for this element
 1537  
      * @param qualifiedName the qualified name of the element to be expressed
 1538  
      * @throws IOException if an IO problem occurs during writing
 1539  
      * @throws SAXException if an SAX problem occurs during writing 
 1540  
      * @deprecated 0.5 replaced by new SAX inspired API
 1541  
      */
 1542  
     protected void expressElementStart(String uri, String localName, String qualifiedName) 
 1543  
                                         throws IOException, SAXException {
 1544  0
         expressElementStart( qualifiedName );
 1545  0
     }
 1546  
     
 1547  
      /**
 1548  
      * Express a closing tag.
 1549  
      *
 1550  
      * @throws IOException if an IO problem occurs during writing
 1551  
      * @throws SAXException if an SAX problem occurs during writing 
 1552  
      * @deprecated 0.5 replaced by new SAX inspired API
 1553  
      */
 1554  0
     protected void expressTagClose() throws IOException, SAXException {}
 1555  
     
 1556  
     /** 
 1557  
      * Express an element end tag (with given name) 
 1558  
      *
 1559  
      * @param qualifiedName the qualified name for the element to be closed
 1560  
      *
 1561  
      * @throws IOException if an IO problem occurs during writing
 1562  
      * @throws SAXException if an SAX problem occurs during writing
 1563  
      * @deprecated 0.5 replaced by new SAX inspired API
 1564  
      */
 1565  
     protected void expressElementEnd(String qualifiedName) 
 1566  
                                               throws IOException, SAXException {
 1567  
         // do nothing
 1568  0
     }
 1569  
     
 1570  
     /** 
 1571  
      * Express an element end tag (with given name) 
 1572  
      *
 1573  
      * @param uri the namespace uri of the element close tag
 1574  
      * @param localName the local name of the element close tag
 1575  
      * @param qualifiedName the qualified name for the element to be closed
 1576  
      *
 1577  
      * @throws IOException if an IO problem occurs during writing
 1578  
      * @throws SAXException if an SAX problem occurs during writing
 1579  
      * @deprecated 0.5 replaced by new SAX inspired API
 1580  
      */
 1581  
     protected void expressElementEnd(
 1582  
                                                 String uri,
 1583  
                                                 String localName,
 1584  
                                                 String qualifiedName) 
 1585  
                                                     throws 
 1586  
                                                         IOException, 
 1587  
                                                         SAXException {
 1588  0
         expressElementEnd(qualifiedName);
 1589  0
     }
 1590  
                                               
 1591  
     
 1592  
     /** 
 1593  
      * Express an empty element end.
 1594  
      * 
 1595  
      * @throws IOException if an IO problem occurs during writing
 1596  
      * @throws SAXException if an SAX problem occurs during writing
 1597  
      * @deprecated 0.5 replaced by new SAX inspired API
 1598  
      */
 1599  0
     protected void expressElementEnd() throws IOException, SAXException {}
 1600  
 
 1601  
     /** 
 1602  
      * Express body text 
 1603  
      *
 1604  
      * @param text the string to write out as the body of the current element
 1605  
      * 
 1606  
      * @throws IOException if an IO problem occurs during writing
 1607  
      * @throws SAXException if an SAX problem occurs during writing
 1608  
      * @deprecated 0.5 replaced by new SAX inspired API
 1609  
      */
 1610  0
     protected void expressBodyText(String text) throws IOException, SAXException {}
 1611  
     
 1612  
     /** 
 1613  
      * Express an attribute 
 1614  
      *
 1615  
      * @param qualifiedName the qualified name of the attribute
 1616  
      * @param value the attribute value
 1617  
      * @throws IOException if an IO problem occurs during writing
 1618  
      * @throws SAXException if an SAX problem occurs during writing
 1619  
      * @deprecated 0.5 replaced by new SAX inspired API
 1620  
      */
 1621  
     protected void expressAttribute(
 1622  
                                 String qualifiedName, 
 1623  
                                 String value) 
 1624  
                                     throws
 1625  
                                         IOException, 
 1626  
                                         SAXException {
 1627  
         // Do nothing
 1628  0
     }
 1629  
 
 1630  
     /** 
 1631  
      * Express an attribute 
 1632  
      *
 1633  
      * @param namespaceUri the namespace uri
 1634  
      * @param localName the local name
 1635  
      * @param qualifiedName the qualified name of the attribute
 1636  
      * @param value the attribute value
 1637  
      * @throws IOException if an IO problem occurs during writing
 1638  
      * @throws SAXException if an SAX problem occurs during writing
 1639  
      * @deprecated 0.5 replaced by new SAX inspired API
 1640  
      */
 1641  
     protected void expressAttribute(
 1642  
                                 String namespaceUri,
 1643  
                                 String localName,
 1644  
                                 String qualifiedName, 
 1645  
                                 String value) 
 1646  
                                     throws
 1647  
                                         IOException, 
 1648  
                                         SAXException {
 1649  0
         expressAttribute(qualifiedName, value);
 1650  0
     }
 1651  
     
 1652  
     
 1653  
     /** 
 1654  
      * Writes the given element 
 1655  
      *
 1656  
      * @param qualifiedName qualified name to use for the element
 1657  
      * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
 1658  
      * @param context the <code>Context</code> to use to evaluate the bean expressions
 1659  
      * @throws IOException if an IO problem occurs during writing
 1660  
      * @throws SAXException if an SAX problem occurs during writing 
 1661  
      * @throws IntrospectionException if a java beans introspection problem occurs
 1662  
      * @deprecated 0.5 replaced by new SAX inspired API
 1663  
      */
 1664  
     protected void write( 
 1665  
                             String qualifiedName, 
 1666  
                             ElementDescriptor elementDescriptor, 
 1667  
                             Context context ) 
 1668  
                                 throws 
 1669  
                                     IOException, 
 1670  
                                     SAXException,
 1671  
                                     IntrospectionException {
 1672  0
         writeElement( "", qualifiedName, qualifiedName, elementDescriptor, context );
 1673  0
     }
 1674  
     
 1675  
     /** 
 1676  
      * Writes the given element adding an ID attribute 
 1677  
      *
 1678  
      * @param qualifiedName qualified name to use for the element
 1679  
      * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
 1680  
      * @param context the <code>Context</code> to use to evaluate the bean expressions
 1681  
      * @param idAttribute the qualified name of the <code>ID</code> attribute 
 1682  
      * @param idValue the value for the <code>ID</code> attribute 
 1683  
      * @throws IOException if an IO problem occurs during writing
 1684  
      * @throws SAXException if an SAX problem occurs during writing 
 1685  
      * @throws IntrospectionException if a java beans introspection problem occurs
 1686  
      * @deprecated 0.5 replaced by new SAX inspired API
 1687  
      */
 1688  
     protected void write( 
 1689  
                             String qualifiedName, 
 1690  
                             ElementDescriptor elementDescriptor, 
 1691  
                             Context context,
 1692  
                             String idAttribute,
 1693  
                             String idValue ) 
 1694  
                                 throws 
 1695  
                                     IOException, 
 1696  
                                     SAXException,
 1697  
                                     IntrospectionException {
 1698  0
         writeElement( 
 1699  0
                     "", 
 1700  0
                     qualifiedName, 
 1701  0
                     qualifiedName, 
 1702  0
                     elementDescriptor, 
 1703  0
                     context, 
 1704  0
                     idAttribute, 
 1705  0
                     idValue );
 1706  0
     }
 1707  
     
 1708  
     /** 
 1709  
      * Write attributes, child elements and element end 
 1710  
      *
 1711  
      * @param qualifiedName qualified name to use for the element
 1712  
      * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
 1713  
      * @param context the <code>Context</code> to use to evaluate the bean expressions
 1714  
      * @throws IOException if an IO problem occurs during writing
 1715  
      * @throws SAXException if an SAX problem occurs during writing 
 1716  
      * @throws IntrospectionException if a java beans introspection problem occurs
 1717  
      * @deprecated 0.5 replaced by new SAX inspired API
 1718  
      */
 1719  
     protected void writeRestOfElement( 
 1720  
                             String qualifiedName, 
 1721  
                             ElementDescriptor elementDescriptor, 
 1722  
                             Context context ) 
 1723  
                                 throws 
 1724  
                                     IOException, 
 1725  
                                     SAXException,
 1726  
                                     IntrospectionException {
 1727  0
         writeRestOfElement( "", qualifiedName, qualifiedName, elementDescriptor, context );
 1728  0
     }
 1729  
 
 1730  
     /**
 1731  
      * Writes an element with a <code>IDREF</code> attribute 
 1732  
      *
 1733  
      * @param qualifiedName of the element with <code>IDREF</code> attribute 
 1734  
      * @param idrefAttributeName the qualified name of the <code>IDREF</code> attribute 
 1735  
      * @param idrefAttributeValue the value for the <code>IDREF</code> attribute 
 1736  
      * @throws IOException if an IO problem occurs during writing
 1737  
      * @throws SAXException if an SAX problem occurs during writing 
 1738  
      * @throws IntrospectionException if a java beans introspection problem occurs
 1739  
      * @deprecated 0.5 replaced by new SAX inspired API
 1740  
      */
 1741  
     protected void writeIDREFElement( 
 1742  
                                     String qualifiedName, 
 1743  
                                     String idrefAttributeName,
 1744  
                                     String idrefAttributeValue ) 
 1745  
                                         throws 
 1746  
                                             IOException, 
 1747  
                                             SAXException,
 1748  
                                             IntrospectionException {
 1749  
         // deprecated
 1750  0
        AttributesImpl attributes = new AttributesImpl();
 1751  0
        attributes.addAttribute( 
 1752  0
                                "",
 1753  0
                                idrefAttributeName, 
 1754  0
                                idrefAttributeName,
 1755  0
                                "IDREF",
 1756  0
                                idrefAttributeValue);
 1757  0
        startElement( "", qualifiedName, qualifiedName, attributes);        
 1758  0
        endElement( "", qualifiedName, qualifiedName );
 1759  0
     }
 1760  
 
 1761  
         
 1762  
     /** 
 1763  
      * Writes the element content.
 1764  
      *
 1765  
      * @param elementDescriptor the <code>ElementDescriptor</code> to write as xml 
 1766  
      * @param context the <code>Context</code> to use to evaluate the bean expressions
 1767  
      * @return true if some content was written
 1768  
      * @throws IOException if an IO problem occurs during writing
 1769  
      * @throws SAXException if an SAX problem occurs during writing 
 1770  
      * @throws IntrospectionException if a java beans introspection problem occurs
 1771  
      * @deprecated 0.5 replaced by new SAX inspired API
 1772  
      */
 1773  
     protected boolean writeContent( 
 1774  
                         ElementDescriptor elementDescriptor, 
 1775  
                         Context context ) 
 1776  
                             throws 
 1777  
                                 IOException, 
 1778  
                                 SAXException,
 1779  
                                 IntrospectionException {     
 1780  0
         return false;
 1781  
     }
 1782  
 
 1783  
     
 1784  
     /**  
 1785  
      * Writes the attribute declarations 
 1786  
      *
 1787  
      * @param elementDescriptor the <code>ElementDescriptor</code> to be written out as xml
 1788  
      * @param context the <code>Context</code> to use to evaluation bean expressions
 1789  
      * @throws IOException if an IO problem occurs during writing
 1790  
      * @throws SAXException if an SAX problem occurs during writing 
 1791  
      * @deprecated 0.5 replaced by new SAX inspired API
 1792  
      */
 1793  
     protected void writeAttributes( 
 1794  
                     ElementDescriptor elementDescriptor, 
 1795  
                     Context context ) 
 1796  
                         throws 
 1797  
                             IOException, SAXException {
 1798  0
         if (!elementDescriptor.isWrapCollectionsInElement()) {
 1799  0
             return;
 1800  
         }
 1801  
             
 1802  0
         AttributeDescriptor[] attributeDescriptors = elementDescriptor.getAttributeDescriptors();
 1803  0
         if ( attributeDescriptors != null ) {
 1804  0
             for ( int i = 0, size = attributeDescriptors.length; i < size; i++ ) {
 1805  0
                 AttributeDescriptor attributeDescriptor = attributeDescriptors[i];
 1806  0
                 writeAttribute( attributeDescriptor, context );
 1807  
             }
 1808  
         }
 1809  0
     }
 1810  
 
 1811  
     
 1812  
     /** 
 1813  
      * Writes an attribute declaration 
 1814  
      *
 1815  
      * @param attributeDescriptor the <code>AttributeDescriptor</code> to be written as xml
 1816  
      * @param context the <code>Context</code> to use to evaluation bean expressions
 1817  
      * @throws IOException if an IO problem occurs during writing
 1818  
      * @throws SAXException if an SAX problem occurs during writing 
 1819  
      * @deprecated 0.5 replaced by new SAX inspired API
 1820  
      */
 1821  
     protected void writeAttribute( 
 1822  
                         AttributeDescriptor attributeDescriptor, 
 1823  
                         Context context ) 
 1824  
                             throws 
 1825  
                                 IOException, SAXException {
 1826  0
         Expression expression = attributeDescriptor.getTextExpression();
 1827  0
         if ( expression != null ) {
 1828  0
             Object value = expression.evaluate( context );
 1829  0
             if ( value != null ) {
 1830  0
                 String text = value.toString();
 1831  0
                 if ( text != null && text.length() > 0 ) {
 1832  0
                     expressAttribute(
 1833  0
                                     attributeDescriptor.getURI(),
 1834  0
                                     attributeDescriptor.getLocalName(),
 1835  0
                                     attributeDescriptor.getQualifiedName(), 
 1836  0
                                     text);
 1837  
                 }
 1838  
             }                
 1839  
         }
 1840  0
     }
 1841  
     /** 
 1842  
      * Writes a empty line.  
 1843  
      * This implementation does nothing but can be overridden by subclasses.
 1844  
      *
 1845  
      * @throws IOException if the line cannot be written
 1846  
      * @deprecated 0.5 replaced by new SAX inspired API
 1847  
      */
 1848  0
     protected void writePrintln() throws IOException {}
 1849  
     
 1850  
     /** 
 1851  
      * Writes an indentation.
 1852  
      * This implementation does nothing but can be overridden by subclasses.
 1853  
      * 
 1854  
      * @throws IOException if the indent cannot be written
 1855  
      * @deprecated 0.5 replaced by new BeanWriter API
 1856  
      */
 1857  0
     protected void writeIndent() throws IOException {}
 1858  
     
 1859  
     /**
 1860  
       * Converts an object to a string.
 1861  
       *
 1862  
       * @param value the Object to represent as a String, possibly null
 1863  
       * @param descriptor writing out this descriptor not null
 1864  
       * @param context not null
 1865  
       * @return String representation, not null
 1866  
       */
 1867  7293
     private String convertToString( Object value , Descriptor descriptor, Context context ) {
 1868  31174
         return getBindingConfiguration()
 1869  15587
             .getObjectStringConverter()
 1870  15587
                 .objectToString( value, descriptor.getPropertyType(), context );
 1871  
     }
 1872  
     
 1873  
     /**
 1874  
       * Factory method for new contexts.
 1875  
       * Ensure that they are correctly configured.
 1876  
       * @param bean make a new Context for this bean
 1877  
       * @return not null
 1878  
       */
 1879  
     private Context makeContext(Object bean) {
 1880  2034
         return new Context( bean, log, bindingConfiguration );
 1881  
     }
 1882  
 
 1883  
     
 1884  
     /**
 1885  
      * Basic mutable implementation of <code>WriteContext</code>.
 1886  
      */
 1887  2021
     private static class WriteContextImpl extends WriteContext {
 1888  
 
 1889  
         private ElementDescriptor currentDescriptor;
 1890  
 
 1891  
         /**
 1892  
          * @see org.apache.commons.betwixt.io.WriteContext#getCurrentDescriptor()
 1893  
          */
 1894  
         public ElementDescriptor getCurrentDescriptor() {
 1895  7176
             return currentDescriptor;
 1896  
         }
 1897  
         
 1898  
         /**
 1899  
          * Sets the descriptor for the current element.
 1900  
          * @param currentDescriptor
 1901  
          */
 1902  
         public void setCurrentDescriptor(ElementDescriptor currentDescriptor) {
 1903  52080
             this.currentDescriptor = currentDescriptor;
 1904  52080
         }
 1905  
         
 1906  
     }
 1907  
 }