Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
AbstractBeanWriter |
|
| 2.292134831460674;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><element/></code> is an empty element but |
|
520 | * <code><element attr='value'/></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><element/></code> is an empty element but |
|
535 | * <code><element attr='value'/></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><element/></code> is an empty element but |
|
1078 | * <code><element attr='value'/></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 | } |