Source Code Cross Referenced for AbstractBeanWriter.java in  » Library » Apache-commons-betwixt-0.8-src » org » apache » commons » betwixt » io » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Library » Apache commons betwixt 0.8 src » org.apache.commons.betwixt.io 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Licensed to the Apache Software Foundation (ASF) under one or more
0003:         * contributor license agreements.  See the NOTICE file distributed with
0004:         * this work for additional information regarding copyright ownership.
0005:         * The ASF licenses this file to You under the Apache License, Version 2.0
0006:         * (the "License"); you may not use this file except in compliance with
0007:         * the License.  You may obtain a copy of the License at
0008:         * 
0009:         *      http://www.apache.org/licenses/LICENSE-2.0
0010:         * 
0011:         * Unless required by applicable law or agreed to in writing, software
0012:         * distributed under the License is distributed on an "AS IS" BASIS,
0013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014:         * See the License for the specific language governing permissions and
0015:         * limitations under the License.
0016:         */
0017:        package org.apache.commons.betwixt.io;
0018:
0019:        import java.beans.IntrospectionException;
0020:        import java.io.IOException;
0021:        import java.util.ArrayList;
0022:        import java.util.Collection;
0023:        import java.util.Iterator;
0024:
0025:        import org.apache.commons.betwixt.AttributeDescriptor;
0026:        import org.apache.commons.betwixt.BindingConfiguration;
0027:        import org.apache.commons.betwixt.Descriptor;
0028:        import org.apache.commons.betwixt.ElementDescriptor;
0029:        import org.apache.commons.betwixt.Options;
0030:        import org.apache.commons.betwixt.XMLBeanInfo;
0031:        import org.apache.commons.betwixt.XMLIntrospector;
0032:        import org.apache.commons.betwixt.expression.Context;
0033:        import org.apache.commons.betwixt.expression.Expression;
0034:        import org.apache.commons.betwixt.io.id.SequentialIDGenerator;
0035:        import org.apache.commons.collections.ArrayStack;
0036:        import org.apache.commons.logging.Log;
0037:        import org.apache.commons.logging.LogFactory;
0038:        import org.xml.sax.Attributes;
0039:        import org.xml.sax.InputSource;
0040:        import org.xml.sax.SAXException;
0041:        import org.xml.sax.helpers.AttributesImpl;
0042:
0043:        /**
0044:         * <p>Abstract superclass for bean writers.
0045:         * This class encapsulates the processing logic. 
0046:         * Subclasses provide implementations for the actual expression of the xml.</p>
0047:         * <h5>SAX Inspired Writing API</h5>
0048:         * <p>
0049:         * This class is intended to be used by subclassing: 
0050:         * concrete subclasses perform the actual writing by providing
0051:         * suitable implementations for the following methods inspired 
0052:         * by <a href='http://www.saxproject.org'>SAX</a>:
0053:         * </p>
0054:         * <ul>
0055:         *     <li> {@link #start} - called when processing begins</li>
0056:         *     <li> {@link #startElement(WriteContext, String, String, String, Attributes)} 
0057:         *     - called when the start of an element 
0058:         *     should be written</li> 
0059:         *     <li> {@link #bodyText(WriteContext, String)} 
0060:         *     - called when the start of an element 
0061:         *     should be written</li> 
0062:         *     <li> {@link #endElement(WriteContext, String, String, String)} 
0063:         *     - called when the end of an element 
0064:         *     should be written</li> 
0065:         *     <li> {@link #end} - called when processing has been completed</li>
0066:         * </ul>
0067:         * <p>
0068:         * <strong>Note</strong> that this class contains many deprecated 
0069:         * versions of the writing API. These will be removed soon so care
0070:         * should be taken to use the latest version.
0071:         * </p>
0072:         * <p>
0073:         * <strong>Note</strong> that this class is designed to be used
0074:         * in a single threaded environment. When used in multi-threaded
0075:         * environments, use of a common <code>XMLIntrospector</code>
0076:         * and pooled writer instances should be considered. 
0077:         * </p>
0078:         *
0079:         * @author <a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a>
0080:         */
0081:        public abstract class AbstractBeanWriter {
0082:
0083:            /** Introspector used */
0084:            private XMLIntrospector introspector = new XMLIntrospector();
0085:
0086:            /** Log used for logging (Doh!) */
0087:            private Log log = LogFactory.getLog(AbstractBeanWriter.class);
0088:            /** Stack containing beans - used to detect cycles */
0089:            private ArrayStack beanStack = new ArrayStack();
0090:            /** Used to generate ID attribute values*/
0091:            private IDGenerator idGenerator = new SequentialIDGenerator();
0092:            /** Should empty elements be written out? */
0093:            private boolean writeEmptyElements = true;
0094:            /** Dynamic binding configuration settings */
0095:            private BindingConfiguration bindingConfiguration = new BindingConfiguration();
0096:            /** <code>WriteContext</code> implementation reused curing writing */
0097:            private WriteContextImpl writeContext = new WriteContextImpl();
0098:            /** Collection of namespaces which have already been declared */
0099:            private Collection namespacesDeclared = new ArrayList();
0100:
0101:            /**
0102:             * Marks the start of the bean writing.
0103:             * By default doesn't do anything, but can be used
0104:             * to do extra start processing 
0105:             * @throws IOException if an IO problem occurs during writing 
0106:             * @throws SAXException if an SAX problem occurs during writing 
0107:             */
0108:            public void start() throws IOException, SAXException {
0109:            }
0110:
0111:            /**
0112:             * Marks the start of the bean writing.
0113:             * By default doesn't do anything, but can be used
0114:             * to do extra end processing 
0115:             * @throws IOException if an IO problem occurs during writing
0116:             * @throws SAXException if an SAX problem occurs during writing 
0117:             */
0118:
0119:            public void end() throws IOException, SAXException {
0120:            }
0121:
0122:            /** 
0123:             * <p> Writes the given bean to the current stream using the XML introspector.</p>
0124:             * 
0125:             * <p> This writes an xml fragment representing the bean to the current stream.</p>
0126:             *
0127:             * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
0128:             * is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
0129:             * setting of the </code>BindingConfiguration</code> is false.</p>
0130:             *
0131:             * @throws IOException if an IO problem occurs during writing 
0132:             * @throws SAXException if an SAX problem occurs during writing  
0133:             * @throws IntrospectionException if a java beans introspection problem occurs 
0134:             *
0135:             * @param bean write out representation of this bean
0136:             */
0137:            public void write(Object bean) throws IOException, SAXException,
0138:                    IntrospectionException {
0139:                if (log.isDebugEnabled()) {
0140:                    log.debug("Writing bean graph...");
0141:                    log.debug(bean);
0142:                }
0143:                start();
0144:                writeBean(null, null, null, bean, makeContext(bean));
0145:                end();
0146:                if (log.isDebugEnabled()) {
0147:                    log.debug("Finished writing bean graph.");
0148:                }
0149:            }
0150:
0151:            /** 
0152:             * <p>Writes the given bean to the current stream 
0153:             * using the given <code>qualifiedName</code>.</p>
0154:             *
0155:             * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
0156:             * is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
0157:             * setting of the <code>BindingConfiguration</code> is false.</p>
0158:             *
0159:             * @param qualifiedName the string naming root element
0160:             * @param bean the <code>Object</code> to write out as xml
0161:             * 
0162:             * @throws IOException if an IO problem occurs during writing
0163:             * @throws SAXException if an SAX problem occurs during writing 
0164:             * @throws IntrospectionException if a java beans introspection problem occurs
0165:             */
0166:            public void write(String qualifiedName, Object bean)
0167:                    throws IOException, SAXException, IntrospectionException {
0168:                start();
0169:                writeBean("", qualifiedName, qualifiedName, bean,
0170:                        makeContext(bean));
0171:                end();
0172:            }
0173:
0174:            /**
0175:             * <p>Writes the bean using the mapping specified in the <code>InputSource</code>.
0176:             * </p><p>
0177:             * <strong>Note:</strong> that the custom mapping will <em>not</em>
0178:             * be registered for later use. Please use {@link XMLIntrospector#register}
0179:             * to register the custom mapping for the class and then call
0180:             * {@link #write(Object)}.
0181:             * </p>
0182:             * @see #write(Object) since the standard notes also apply
0183:             * @since 0.7
0184:             * @param bean <code>Object</code> to be written as xml, not null
0185:             * @param source <code>InputSource/code> containing an xml document
0186:             * specifying the mapping to be used (in the usual way), not null
0187:             * @throws IOException
0188:             * @throws SAXException
0189:             * @throws IntrospectionException
0190:             */
0191:            public void write(Object bean, InputSource source)
0192:                    throws IOException, SAXException, IntrospectionException {
0193:                writeBean(null, null, null, bean, makeContext(bean),
0194:                        getXMLIntrospector()
0195:                                .introspect(bean.getClass(), source));
0196:            }
0197:
0198:            /** 
0199:             * <p>Writes the given bean to the current stream 
0200:             * using the given <code>qualifiedName</code>.</p>
0201:             *
0202:             * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
0203:             * is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
0204:             * setting of the <code>BindingConfiguration</code> is false.</p>
0205:             *
0206:             * @param namespaceUri the namespace uri
0207:             * @param localName the local name
0208:             * @param qualifiedName the string naming root element
0209:             * @param introspectedBindType the <code>Class</code> of the bean 
0210:             * as resolved at introspection time, or null if the type has not been resolved
0211:             * @param bean the <code>Object</code> to write out as xml
0212:             * @param context not null
0213:             * 
0214:             * @throws IOException if an IO problem occurs during writing
0215:             * @throws SAXException if an SAX problem occurs during writing 
0216:             * @throws IntrospectionException if a java beans introspection problem occurs
0217:             */
0218:            private void writeBean(String namespaceUri, String localName,
0219:                    String qualifiedName, Object bean, Context context)
0220:                    throws IOException, SAXException, IntrospectionException {
0221:
0222:                if (log.isTraceEnabled()) {
0223:                    log.trace("Writing bean graph (qualified name '"
0224:                            + qualifiedName + "'");
0225:                }
0226:
0227:                // introspect to obtain bean info
0228:                XMLBeanInfo beanInfo = introspector.introspect(bean);
0229:                writeBean(namespaceUri, localName, qualifiedName, bean,
0230:                        context, beanInfo);
0231:
0232:                log.trace("Finished writing bean graph.");
0233:            }
0234:
0235:            private void writeBean(String namespaceUri, String localName,
0236:                    String qualifiedName, Object bean,
0237:                    ElementDescriptor parentDescriptor, Context context)
0238:                    throws IOException, SAXException, IntrospectionException {
0239:
0240:                if (log.isTraceEnabled()) {
0241:                    log.trace("Writing bean graph (qualified name '"
0242:                            + qualifiedName + "'");
0243:                }
0244:
0245:                // introspect to obtain bean info
0246:                XMLBeanInfo beanInfo = findXMLBeanInfo(bean, parentDescriptor);
0247:                writeBean(namespaceUri, localName, qualifiedName, bean,
0248:                        context, beanInfo);
0249:
0250:                log.trace("Finished writing bean graph.");
0251:            }
0252:
0253:            /**
0254:             * Finds the appropriate bean info for the given (hollow) element.
0255:             * @param bean
0256:             * @param parentDescriptor <code>ElementDescriptor</code>, not null
0257:             * @return <code>XMLBeanInfo</code>, not null
0258:             * @throws IntrospectionException
0259:             */
0260:            private XMLBeanInfo findXMLBeanInfo(Object bean,
0261:                    ElementDescriptor parentDescriptor)
0262:                    throws IntrospectionException {
0263:                XMLBeanInfo beanInfo = null;
0264:                Class introspectedBindType = parentDescriptor
0265:                        .getSingularPropertyType();
0266:                if (introspectedBindType == null) {
0267:                    introspectedBindType = parentDescriptor.getPropertyType();
0268:                }
0269:                if (parentDescriptor.isUseBindTimeTypeForMapping()
0270:                        || introspectedBindType == null) {
0271:                    beanInfo = introspector.introspect(bean);
0272:                } else {
0273:                    beanInfo = introspector.introspect(introspectedBindType);
0274:                }
0275:                return beanInfo;
0276:            }
0277:
0278:            /**
0279:             * <p>Writes the given bean to the current stream 
0280:             * using the given mapping.</p>
0281:             *
0282:             * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
0283:             * is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
0284:             * setting of the <code>BindingConfiguration</code> is false.</p>
0285:             *
0286:             * @param namespaceUri the namespace uri, or null to use the automatic binding
0287:             * @param localName the local name  or null to use the automatic binding
0288:             * @param qualifiedName the <code>String</code> naming the root element 
0289:             *  or null to use the automatic binding
0290:             * @param bean <code>Object</code> to be written, not null
0291:             * @param context <code>Context</code>, not null
0292:             * @param beanInfo <code>XMLBeanInfo</code>, not null
0293:             * @throws IOException
0294:             * @throws SAXException
0295:             * @throws IntrospectionException
0296:             */
0297:            private void writeBean(String namespaceUri, String localName,
0298:                    String qualifiedName, Object bean, Context context,
0299:                    XMLBeanInfo beanInfo) throws IOException, SAXException,
0300:                    IntrospectionException {
0301:                if (beanInfo != null) {
0302:                    ElementDescriptor elementDescriptor = beanInfo
0303:                            .getElementDescriptor();
0304:                    if (elementDescriptor != null) {
0305:
0306:                        // Construct the options
0307:                        Options combinedOptions = new Options();
0308:
0309:                        // Add options defined by the current bean's element descriptor
0310:                        combinedOptions.addOptions(elementDescriptor
0311:                                .getOptions());
0312:
0313:                        // The parent descriptor may have defined options
0314:                        // for the current bean.  These options take precedence
0315:                        // over the options of the current class descriptor
0316:                        if (context.getOptions() != null) {
0317:                            combinedOptions.addOptions(context.getOptions());
0318:                        }
0319:                        context = context.newContext(bean);
0320:                        context.pushOptions(combinedOptions);
0321:
0322:                        if (qualifiedName == null) {
0323:                            qualifiedName = elementDescriptor
0324:                                    .getQualifiedName();
0325:                        }
0326:                        if (namespaceUri == null) {
0327:                            namespaceUri = elementDescriptor.getURI();
0328:                        }
0329:                        if (localName == null) {
0330:                            localName = elementDescriptor.getLocalName();
0331:                        }
0332:
0333:                        String ref = null;
0334:                        String id = null;
0335:
0336:                        // simple type should not have IDs
0337:                        if (elementDescriptor.isSimple()) {
0338:                            // write without an id
0339:                            writeElement(namespaceUri, localName,
0340:                                    qualifiedName, elementDescriptor, context);
0341:
0342:                        } else {
0343:                            pushBean(context.getBean());
0344:                            if (getBindingConfiguration().getMapIDs()) {
0345:                                ref = getBindingConfiguration()
0346:                                        .getIdMappingStrategy()
0347:                                        .getReferenceFor(context,
0348:                                                context.getBean());
0349:                            }
0350:                            if (ref == null) {
0351:                                // this is the first time that this bean has be written
0352:                                AttributeDescriptor idAttribute = beanInfo
0353:                                        .getIDAttribute();
0354:                                if (idAttribute == null) {
0355:                                    // use a generated id
0356:                                    id = idGenerator.nextId();
0357:                                    getBindingConfiguration()
0358:                                            .getIdMappingStrategy()
0359:                                            .setReference(context, bean, id);
0360:
0361:                                    if (getBindingConfiguration().getMapIDs()) {
0362:                                        // write element with id
0363:                                        writeElement(namespaceUri, localName,
0364:                                                qualifiedName,
0365:                                                elementDescriptor, context,
0366:                                                beanInfo.getIDAttributeName(),
0367:                                                id);
0368:
0369:                                    } else {
0370:                                        // write element without ID
0371:                                        writeElement(namespaceUri, localName,
0372:                                                qualifiedName,
0373:                                                elementDescriptor, context);
0374:                                    }
0375:
0376:                                } else {
0377:                                    // use id from bean property
0378:                                    // it's up to the user to ensure uniqueness
0379:                                    Expression idExpression = idAttribute
0380:                                            .getTextExpression();
0381:                                    if (idExpression == null) {
0382:                                        throw new IntrospectionException(
0383:                                                "The specified id property wasn't found in the bean ("
0384:                                                        + idAttribute + ").");
0385:                                    }
0386:                                    Object exp = idExpression.evaluate(context);
0387:                                    if (exp == null) {
0388:                                        // we'll use a random id
0389:                                        log.debug("Using random id");
0390:                                        id = idGenerator.nextId();
0391:
0392:                                    } else {
0393:                                        // convert to string
0394:                                        id = exp.toString();
0395:                                    }
0396:                                    getBindingConfiguration()
0397:                                            .getIdMappingStrategy()
0398:                                            .setReference(context, bean, id);
0399:
0400:                                    // the ID attribute should be written automatically
0401:                                    writeElement(namespaceUri, localName,
0402:                                            qualifiedName, elementDescriptor,
0403:                                            context);
0404:                                }
0405:                            } else {
0406:
0407:                                if (!ignoreElement(elementDescriptor,
0408:                                        namespaceUri, localName, qualifiedName,
0409:                                        context)) {
0410:                                    // we've already written this bean so write an IDREF
0411:                                    writeIDREFElement(elementDescriptor,
0412:                                            namespaceUri, localName,
0413:                                            qualifiedName, beanInfo
0414:                                                    .getIDREFAttributeName(),
0415:                                            ref);
0416:                                }
0417:                            }
0418:                            popBean();
0419:                        }
0420:
0421:                        context.popOptions();
0422:                    }
0423:                }
0424:            }
0425:
0426:            /** 
0427:             * Get <code>IDGenerator</code> implementation used to 
0428:             * generate <code>ID</code> attribute values .
0429:             *
0430:             * @return implementation used for <code>ID</code> attribute generation
0431:             */
0432:            public IDGenerator getIdGenerator() {
0433:                return idGenerator;
0434:            }
0435:
0436:            /** 
0437:             * Set <code>IDGenerator</code> implementation 
0438:             * used to generate <code>ID</code> attribute values.
0439:             * This property can be used to customize the algorithm used for generation.
0440:             *
0441:             * @param idGenerator use this implementation for <code>ID</code> attribute generation
0442:             */
0443:            public void setIdGenerator(IDGenerator idGenerator) {
0444:                this .idGenerator = idGenerator;
0445:            }
0446:
0447:            /**
0448:             * Gets the dynamic configuration setting to be used for bean reading.
0449:             * @return the BindingConfiguration settings, not null
0450:             * @since 0.5
0451:             */
0452:            public BindingConfiguration getBindingConfiguration() {
0453:                return bindingConfiguration;
0454:            }
0455:
0456:            /**
0457:             * Sets the dynamic configuration setting to be used for bean reading.
0458:             * @param bindingConfiguration the BindingConfiguration settings, not null
0459:             * @since 0.5
0460:             */
0461:            public void setBindingConfiguration(
0462:                    BindingConfiguration bindingConfiguration) {
0463:                this .bindingConfiguration = bindingConfiguration;
0464:            }
0465:
0466:            /** 
0467:             * <p>Should generated <code>ID</code> attribute values be added to the elements?</p>
0468:             * 
0469:             * <p>If IDs are not being written then if a cycle is encountered in the bean graph, 
0470:             * then a {@link CyclicReferenceException} will be thrown by the write method.</p>
0471:             * 
0472:             * @return true if <code>ID</code> and <code>IDREF</code> attributes are to be written
0473:             * @deprecated 0.5 use {@link BindingConfiguration#getMapIDs}
0474:             */
0475:            public boolean getWriteIDs() {
0476:                return getBindingConfiguration().getMapIDs();
0477:            }
0478:
0479:            /** 
0480:             * Set whether generated <code>ID</code> attribute values should be added to the elements 
0481:             * If this property is set to false, then <code>CyclicReferenceException</code> 
0482:             * will be thrown whenever a cyclic occurs in the bean graph.
0483:             *
0484:             * @param writeIDs true if <code>ID</code>'s and <code>IDREF</code>'s should be written
0485:             * @deprecated 0.5 use {@link BindingConfiguration#setMapIDs}
0486:             */
0487:            public void setWriteIDs(boolean writeIDs) {
0488:                getBindingConfiguration().setMapIDs(writeIDs);
0489:            }
0490:
0491:            /**
0492:             * <p>Gets whether empty elements should be written into the output.</p>
0493:             *
0494:             * <p>An empty element is one that has no attributes, no child elements 
0495:             * and no body text.
0496:             * For example, <code>&lt;element/&gt;</code> is an empty element but
0497:             * <code>&lt;element attr='value'/&gt;</code> is not.</p>
0498:             *
0499:             * @return true if empty elements will be written into the output
0500:             * @since 0.5
0501:             */
0502:            public boolean getWriteEmptyElements() {
0503:                return writeEmptyElements;
0504:            }
0505:
0506:            /**
0507:             * <p>Sets whether empty elements should be written into the output.</p>
0508:             *
0509:             * <p>An empty element is one that has no attributes, no child elements 
0510:             * and no body text.
0511:             * For example, <code>&lt;element/&gt;</code> is an empty element but
0512:             * <code>&lt;element attr='value'/&gt;</code> is not.
0513:             *
0514:             * @param writeEmptyElements true if empty elements should be written into the output 
0515:             * @since 0.5
0516:             */
0517:            public void setWriteEmptyElements(boolean writeEmptyElements) {
0518:                this .writeEmptyElements = writeEmptyElements;
0519:            }
0520:
0521:            /**
0522:             * <p>Gets the introspector used.</p>
0523:             *
0524:             * <p>The {@link XMLBeanInfo} used to map each bean is 
0525:             * created by the <code>XMLIntrospector</code>.
0526:             * One way in which the mapping can be customized is 
0527:             * by altering the <code>XMLIntrospector</code>. </p>
0528:             *
0529:             * @return the <code>XMLIntrospector</code> used for introspection
0530:             */
0531:            public XMLIntrospector getXMLIntrospector() {
0532:                return introspector;
0533:            }
0534:
0535:            /**
0536:             * <p>Sets the introspector to be used.</p>
0537:             *
0538:             * <p>The {@link XMLBeanInfo} used to map each bean is 
0539:             * created by the <code>XMLIntrospector</code>.
0540:             * One way in which the mapping can be customized is by 
0541:             * altering the <code>XMLIntrospector</code>. </p>
0542:             *
0543:             * @param introspector use this introspector
0544:             */
0545:            public void setXMLIntrospector(XMLIntrospector introspector) {
0546:                this .introspector = introspector;
0547:            }
0548:
0549:            /**
0550:             * <p>Gets the current logging implementation.</p>
0551:             *
0552:             * @return the <code>Log</code> implementation which this class logs to
0553:             */
0554:            public final Log getAbstractBeanWriterLog() {
0555:                return log;
0556:            }
0557:
0558:            /**
0559:             * <p> Set the current logging implementation. </p>
0560:             *
0561:             * @param log <code>Log</code> implementation to use
0562:             */
0563:            public final void setAbstractBeanWriterLog(Log log) {
0564:                this .log = log;
0565:            }
0566:
0567:            // SAX-style methods
0568:            //-------------------------------------------------------------------------    
0569:
0570:            /**
0571:             * Writes the start tag for an element.
0572:             *
0573:             * @param uri the element's namespace uri
0574:             * @param localName the element's local name 
0575:             * @param qName the element's qualified name
0576:             * @param attr the element's attributes
0577:             *
0578:             * @throws IOException if an IO problem occurs during writing
0579:             * @throws SAXException if an SAX problem occurs during writing 
0580:             * @since 0.5
0581:             */
0582:            protected void startElement(WriteContext context, String uri,
0583:                    String localName, String qName, Attributes attr)
0584:                    throws IOException, SAXException {
0585:                // for backwards compatbility call older methods
0586:                startElement(uri, localName, qName, attr);
0587:            }
0588:
0589:            /**
0590:             * Writes the end tag for an element
0591:             *
0592:             * @param uri the element's namespace uri
0593:             * @param localName the element's local name 
0594:             * @param qName the element's qualified name
0595:             *
0596:             * @throws IOException if an IO problem occurs during writing
0597:             * @throws SAXException if an SAX problem occurs during writing 
0598:             * @since 0.5
0599:             */
0600:            protected void endElement(WriteContext context, String uri,
0601:                    String localName, String qName) throws IOException,
0602:                    SAXException {
0603:                // for backwards compatibility call older interface
0604:                endElement(uri, localName, qName);
0605:            }
0606:
0607:            /** 
0608:             * Writes body text
0609:             *
0610:             * @param text the body text to be written
0611:             *
0612:             * @throws IOException if an IO problem occurs during writing
0613:             * @throws SAXException if an SAX problem occurs during writing 
0614:             * @since 0.5
0615:             */
0616:            protected void bodyText(WriteContext context, String text)
0617:                    throws IOException, SAXException {
0618:                // for backwards compatibility call older interface
0619:                bodyText(text);
0620:            }
0621:
0622:            // Older SAX-style methods
0623:            //-------------------------------------------------------------------------    
0624:
0625:            /**
0626:             * Writes the start tag for an element.
0627:             *
0628:             * @param uri the element's namespace uri
0629:             * @param localName the element's local name 
0630:             * @param qName the element's qualified name
0631:             * @param attr the element's attributes
0632:             *
0633:             * @throws IOException if an IO problem occurs during writing
0634:             * @throws SAXException if an SAX problem occurs during writing 
0635:             * @deprecated 0.5 use {@link #startElement(WriteContext, String, String, String, Attributes)}
0636:             */
0637:            protected void startElement(String uri, String localName,
0638:                    String qName, Attributes attr) throws IOException,
0639:                    SAXException {
0640:            }
0641:
0642:            /**
0643:             * Writes the end tag for an element
0644:             *
0645:             * @param uri the element's namespace uri
0646:             * @param localName the element's local name 
0647:             * @param qName the element's qualified name
0648:             *
0649:             * @throws IOException if an IO problem occurs during writing
0650:             * @throws SAXException if an SAX problem occurs during writing 
0651:             * @deprecated 0.5 use {@link #endElement(WriteContext, String, String, String)}
0652:             */
0653:            protected void endElement(String uri, String localName, String qName)
0654:                    throws IOException, SAXException {
0655:            }
0656:
0657:            /** 
0658:             * Writes body text
0659:             *
0660:             * @param text the body text to be written
0661:             *
0662:             * @throws IOException if an IO problem occurs during writing
0663:             * @throws SAXException if an SAX problem occurs during writing 
0664:             * @deprecated 0.5 use {@link #bodyText(WriteContext, String)}
0665:             */
0666:            protected void bodyText(String text) throws IOException,
0667:                    SAXException {
0668:            }
0669:
0670:            // Implementation methods
0671:            //-------------------------------------------------------------------------    
0672:
0673:            /** 
0674:             * Writes the given element 
0675:             *
0676:             * @param namespaceUri the namespace uri
0677:             * @param localName the local name
0678:             * @param qualifiedName qualified name to use for the element
0679:             * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
0680:             * @param context the <code>Context</code> to use to evaluate the bean expressions
0681:             * @throws IOException if an IO problem occurs during writing
0682:             * @throws SAXException if an SAX problem occurs during writing 
0683:             * @throws IntrospectionException if a java beans introspection problem occurs
0684:             */
0685:            private void writeElement(String namespaceUri, String localName,
0686:                    String qualifiedName, ElementDescriptor elementDescriptor,
0687:                    Context context) throws IOException, SAXException,
0688:                    IntrospectionException {
0689:                if (log.isTraceEnabled()) {
0690:                    log.trace("Writing: " + qualifiedName + " element: "
0691:                            + elementDescriptor);
0692:                }
0693:
0694:                if (!ignoreElement(elementDescriptor, namespaceUri, localName,
0695:                        qualifiedName, context)) {
0696:                    if (log.isTraceEnabled()) {
0697:                        log
0698:                                .trace("Element " + elementDescriptor
0699:                                        + " is empty.");
0700:                    }
0701:
0702:                    Attributes attributes = addNamespaceDeclarations(
0703:                            new ElementAttributes(elementDescriptor, context),
0704:                            namespaceUri);
0705:                    writeContext.setCurrentDescriptor(elementDescriptor);
0706:                    startElement(writeContext, namespaceUri, localName,
0707:                            qualifiedName, attributes);
0708:
0709:                    writeElementContent(elementDescriptor, context);
0710:                    writeContext.setCurrentDescriptor(elementDescriptor);
0711:                    endElement(writeContext, namespaceUri, localName,
0712:                            qualifiedName);
0713:                }
0714:            }
0715:
0716:            /**
0717:             * Adds namespace declarations (if any are needed) to the given attributes.
0718:             * @param attributes Attributes, not null
0719:             * @param elementNamespaceUri the URI for the enclosing element, possibly null
0720:             * @return Attributes, not null
0721:             */
0722:            private Attributes addNamespaceDeclarations(Attributes attributes,
0723:                    String elementNamespaceUri) {
0724:                Attributes result = attributes;
0725:                AttributesImpl withDeclarations = null;
0726:                for (int i = -1, size = attributes.getLength(); i < size; i++) {
0727:                    String uri = null;
0728:                    if (i == -1) {
0729:                        uri = elementNamespaceUri;
0730:                    } else {
0731:                        uri = attributes.getURI(i);
0732:                    }
0733:                    if (uri != null && !"".equals(uri)
0734:                            && !namespacesDeclared.contains(uri)) {
0735:                        if (withDeclarations == null) {
0736:                            withDeclarations = new AttributesImpl(attributes);
0737:                        }
0738:                        withDeclarations.addAttribute("", "", "xmlns:"
0739:                                + getXMLIntrospector().getConfiguration()
0740:                                        .getPrefixMapper().getPrefix(uri),
0741:                                "NOTATION", uri);
0742:                        namespacesDeclared.add(uri);
0743:                    }
0744:                }
0745:
0746:                if (withDeclarations != null) {
0747:                    result = withDeclarations;
0748:                }
0749:                return result;
0750:            }
0751:
0752:            /** 
0753:             * Writes the given element adding an ID attribute 
0754:             *
0755:             * @param namespaceUri the namespace uri
0756:             * @param localName the local name
0757:             * @param qualifiedName the qualified name
0758:             * @param elementDescriptor the ElementDescriptor describing this element
0759:             * @param context the context being evaliated against
0760:             * @param idAttribute the qualified name of the <code>ID</code> attribute 
0761:             * @param idValue the value for the <code>ID</code> attribute 
0762:             * @throws IOException if an IO problem occurs during writing
0763:             * @throws SAXException if an SAX problem occurs during writing 
0764:             * @throws IntrospectionException if a java beans introspection problem occurs
0765:             */
0766:            private void writeElement(String namespaceUri, String localName,
0767:                    String qualifiedName, ElementDescriptor elementDescriptor,
0768:                    Context context, String idAttribute, String idValue)
0769:                    throws IOException, SAXException, IntrospectionException {
0770:
0771:                if (!ignoreElement(elementDescriptor, namespaceUri, localName,
0772:                        qualifiedName, context)) {
0773:                    writeContext.setCurrentDescriptor(elementDescriptor);
0774:                    Attributes attributes = new IDElementAttributes(
0775:                            elementDescriptor, context, idAttribute, idValue);
0776:                    startElement(writeContext, namespaceUri, localName,
0777:                            qualifiedName, addNamespaceDeclarations(attributes,
0778:                                    namespaceUri));
0779:
0780:                    writeElementContent(elementDescriptor, context);
0781:                    writeContext.setCurrentDescriptor(elementDescriptor);
0782:                    endElement(writeContext, namespaceUri, localName,
0783:                            qualifiedName);
0784:                } else if (log.isTraceEnabled()) {
0785:                    log.trace("Element " + qualifiedName + " is empty.");
0786:                }
0787:            }
0788:
0789:            /**
0790:             * Write attributes, child elements and element end 
0791:             * 
0792:             * @param uri the element namespace uri 
0793:             * @param localName the local name of the element
0794:             * @param qualifiedName the qualified name of the element
0795:             * @param elementDescriptor the descriptor for this element
0796:             * @param context evaluate against this context
0797:             * @throws IOException if an IO problem occurs during writing
0798:             * @throws SAXException if an SAX problem occurs during writing 
0799:             * @throws IntrospectionException if a java beans introspection problem occurs
0800:             */
0801:            private void writeRestOfElement(String uri, String localName,
0802:                    String qualifiedName, ElementDescriptor elementDescriptor,
0803:                    Context context) throws IOException, SAXException,
0804:                    IntrospectionException {
0805:
0806:                writeElementContent(elementDescriptor, context);
0807:            }
0808:
0809:            /**
0810:             * Writes an element with a <code>IDREF</code> attribute 
0811:             *
0812:             * @param uri the namespace uri
0813:             * @param localName the local name
0814:             * @param qualifiedName of the element with <code>IDREF</code> attribute 
0815:             * @param idrefAttributeName the qualified name of the <code>IDREF</code> attribute 
0816:             * @param idrefAttributeValue the value for the <code>IDREF</code> attribute 
0817:             * @throws IOException if an IO problem occurs during writing
0818:             * @throws SAXException if an SAX problem occurs during writing 
0819:             * @throws IntrospectionException if a java beans introspection problem occurs
0820:             */
0821:            private void writeIDREFElement(ElementDescriptor elementDescriptor,
0822:                    String uri, String localName, String qualifiedName,
0823:                    String idrefAttributeName, String idrefAttributeValue)
0824:                    throws IOException, SAXException, IntrospectionException {
0825:
0826:                // write IDREF element
0827:                AttributesImpl attributes = new AttributesImpl();
0828:                // XXX for the moment, assign IDREF to default namespace
0829:                attributes.addAttribute("", idrefAttributeName,
0830:                        idrefAttributeName, "IDREF", idrefAttributeValue);
0831:                writeContext.setCurrentDescriptor(elementDescriptor);
0832:                startElement(writeContext, uri, localName, qualifiedName,
0833:                        addNamespaceDeclarations(attributes, uri));
0834:                endElement(writeContext, uri, localName, qualifiedName);
0835:            }
0836:
0837:            /** 
0838:             * Writes the element content.
0839:             *
0840:             * @param elementDescriptor the <code>ElementDescriptor</code> to write as xml 
0841:             * @param context the <code>Context</code> to use to evaluate the bean expressions
0842:             * 
0843:             * @throws IOException if an IO problem occurs during writing
0844:             * @throws SAXException if an SAX problem occurs during writing 
0845:             * @throws IntrospectionException if a java beans introspection problem occurs
0846:             */
0847:            private void writeElementContent(
0848:                    ElementDescriptor elementDescriptor, Context context)
0849:                    throws IOException, SAXException, IntrospectionException {
0850:                writeContext.setCurrentDescriptor(elementDescriptor);
0851:                Descriptor[] childDescriptors = elementDescriptor
0852:                        .getContentDescriptors();
0853:                if (childDescriptors != null && childDescriptors.length > 0) {
0854:                    // process child elements
0855:                    for (int i = 0, size = childDescriptors.length; i < size; i++) {
0856:                        if (childDescriptors[i] instanceof  ElementDescriptor) {
0857:                            // Element content
0858:                            ElementDescriptor childDescriptor = (ElementDescriptor) childDescriptors[i];
0859:                            Context childContext = context;
0860:                            childContext.pushOptions(childDescriptor
0861:                                    .getOptions());
0862:                            Expression childExpression = childDescriptor
0863:                                    .getContextExpression();
0864:                            if (childExpression != null) {
0865:                                Object childBean = childExpression
0866:                                        .evaluate(context);
0867:                                if (childBean != null) {
0868:                                    String qualifiedName = childDescriptor
0869:                                            .getQualifiedName();
0870:                                    String namespaceUri = childDescriptor
0871:                                            .getURI();
0872:                                    String localName = childDescriptor
0873:                                            .getLocalName();
0874:                                    // XXXX: should we handle nulls better
0875:                                    if (childBean instanceof  Iterator) {
0876:                                        for (Iterator iter = (Iterator) childBean; iter
0877:                                                .hasNext();) {
0878:                                            Object object = iter.next();
0879:                                            if (object == null) {
0880:                                                continue;
0881:                                            }
0882:                                            writeBean(namespaceUri, localName,
0883:                                                    qualifiedName, object,
0884:                                                    childDescriptor, context);
0885:                                        }
0886:                                    } else {
0887:                                        writeBean(namespaceUri, localName,
0888:                                                qualifiedName, childBean,
0889:                                                childDescriptor, context);
0890:                                    }
0891:                                }
0892:                            } else {
0893:                                writeElement(childDescriptor.getURI(),
0894:                                        childDescriptor.getLocalName(),
0895:                                        childDescriptor.getQualifiedName(),
0896:                                        childDescriptor, childContext);
0897:                            }
0898:                            childContext.popOptions();
0899:                        } else {
0900:                            // Mixed text content
0901:                            // evaluate the body text 
0902:                            Expression expression = childDescriptors[i]
0903:                                    .getTextExpression();
0904:                            if (expression != null) {
0905:                                Object value = expression.evaluate(context);
0906:                                String text = convertToString(value,
0907:                                        childDescriptors[i], context);
0908:                                if (text != null && text.length() > 0) {
0909:                                    ;
0910:                                    bodyText(writeContext, text);
0911:                                }
0912:                            }
0913:                        }
0914:                    }
0915:                } else {
0916:                    // evaluate the body text 
0917:                    Expression expression = elementDescriptor
0918:                            .getTextExpression();
0919:                    if (expression != null) {
0920:                        Object value = expression.evaluate(context);
0921:                        String text = convertToString(value, elementDescriptor,
0922:                                context);
0923:                        if (text != null && text.length() > 0) {
0924:                            bodyText(writeContext, text);
0925:                        }
0926:                    }
0927:                }
0928:            }
0929:
0930:            /**
0931:             * Pushes the bean onto the ancestry stack.
0932:             * If IDs are not being written, then check for cyclic references.
0933:             *
0934:             * @param bean push this bean onto the ancester stack
0935:             */
0936:            protected void pushBean(Object bean) {
0937:                // check that we don't have a cyclic reference when we're not writing IDs
0938:                if (!getBindingConfiguration().getMapIDs()) {
0939:                    Iterator it = beanStack.iterator();
0940:                    while (it.hasNext()) {
0941:                        Object next = it.next();
0942:                        // use absolute equality rather than equals
0943:                        // we're only really bothered if objects are actually the same
0944:                        if (bean == next) {
0945:                            final String message = "Cyclic reference at bean: "
0946:                                    + bean;
0947:                            log.error(message);
0948:                            StringBuffer buffer = new StringBuffer(message);
0949:                            buffer.append(" Stack: ");
0950:                            Iterator errorStack = beanStack.iterator();
0951:                            while (errorStack.hasNext()) {
0952:                                Object errorObj = errorStack.next();
0953:                                if (errorObj != null) {
0954:                                    buffer
0955:                                            .append(errorObj.getClass()
0956:                                                    .getName());
0957:                                    buffer.append(": ");
0958:                                }
0959:                                buffer.append(errorObj);
0960:                                buffer.append(";");
0961:                            }
0962:                            final String debugMessage = buffer.toString();
0963:                            log.info(debugMessage);
0964:                            throw new CyclicReferenceException(debugMessage);
0965:                        }
0966:                    }
0967:                }
0968:                if (log.isTraceEnabled()) {
0969:                    log.trace("Pushing onto object stack: " + bean);
0970:                }
0971:                beanStack.push(bean);
0972:            }
0973:
0974:            /** 
0975:             * Pops the top bean off from the ancestry stack 
0976:             *
0977:             * @return the last object pushed onto the ancester stack
0978:             */
0979:            protected Object popBean() {
0980:                Object bean = beanStack.pop();
0981:                if (log.isTraceEnabled()) {
0982:                    log.trace("Popped from object stack: " + bean);
0983:                }
0984:                return bean;
0985:            }
0986:
0987:            /** 
0988:             * Should this element (and children) be written out?
0989:             *
0990:             * @param descriptor the <code>ElementDescriptor</code> to evaluate
0991:             * @param context the <code>Context</code> against which the element will be evaluated
0992:             * @return true if this element should be written out
0993:             * @throws IntrospectionException
0994:             */
0995:            private boolean ignoreElement(ElementDescriptor descriptor,
0996:                    String namespaceUri, String localName,
0997:                    String qualifiedName, Context context)
0998:                    throws IntrospectionException {
0999:                if (getBindingConfiguration().getValueSuppressionStrategy()
1000:                        .suppressElement(descriptor, namespaceUri, localName,
1001:                                qualifiedName, context.getBean())) {
1002:                    return true;
1003:                }
1004:
1005:                if (!getWriteEmptyElements()) {
1006:                    return isEmptyElement(descriptor, context);
1007:                }
1008:                return false;
1009:            }
1010:
1011:            /** 
1012:             * <p>Will evaluating this element against this context result in an empty element?</p>
1013:             *
1014:             * <p>An empty element is one that has no attributes, no child elements 
1015:             * and no body text.
1016:             * For example, <code>&lt;element/&gt;</code> is an empty element but
1017:             * <code>&lt;element attr='value'/&gt;</code> is not.</p>
1018:             * 
1019:             * @param descriptor the <code>ElementDescriptor</code> to evaluate
1020:             * @param context the <code>Context</code> against which the element will be evaluated
1021:             * @return true if this element is empty on evaluation
1022:             * @throws IntrospectionException
1023:             */
1024:            private boolean isEmptyElement(ElementDescriptor descriptor,
1025:                    Context context) throws IntrospectionException {
1026:                //TODO: this design isn't too good
1027:                // to would be much better to render just once 
1028:                if (log.isTraceEnabled()) {
1029:                    log.trace("Is " + descriptor + " empty?");
1030:                }
1031:
1032:                // an element which has attributes is not empty
1033:                if (descriptor.hasAttributes()) {
1034:                    log.trace("Element has attributes.");
1035:                    return false;
1036:                }
1037:
1038:                // an element is not empty if it has a non-empty body
1039:                Expression expression = descriptor.getTextExpression();
1040:                if (expression != null) {
1041:                    Object value = expression.evaluate(context);
1042:                    String text = convertToString(value, descriptor, context);
1043:                    if (text != null && text.length() > 0) {
1044:                        log.trace("Element has body text which isn't empty.");
1045:                        return false;
1046:                    }
1047:                }
1048:
1049:                // always write out loops - even when they have no elements
1050:                if (descriptor.isCollective()) {
1051:                    log.trace("Loop type so not empty.");
1052:                    return false;
1053:                }
1054:
1055:                // now test child elements
1056:                // an element is empty if it has no non-empty child elements
1057:                if (descriptor.hasChildren()) {
1058:                    for (int i = 0, size = descriptor.getElementDescriptors().length; i < size; i++) {
1059:                        if (!isEmptyElement(
1060:                                descriptor.getElementDescriptors()[i], context)) {
1061:                            log.trace("Element has child which isn't empty.");
1062:                            return false;
1063:                        }
1064:                    }
1065:                }
1066:
1067:                if (descriptor.isHollow()) {
1068:                    Expression contentExpression = descriptor
1069:                            .getContextExpression();
1070:                    if (contentExpression != null) {
1071:                        Object childBean = contentExpression.evaluate(context);
1072:                        if (childBean != null) {
1073:                            XMLBeanInfo xmlBeanInfo = findXMLBeanInfo(
1074:                                    childBean, descriptor);
1075:                            Object currentBean = context.getBean();
1076:                            context.setBean(childBean);
1077:                            boolean result = isEmptyElement(xmlBeanInfo
1078:                                    .getElementDescriptor(), context);
1079:                            context.setBean(currentBean);
1080:                            return result;
1081:                        }
1082:                    }
1083:                }
1084:
1085:                log.trace("Element is empty.");
1086:                return true;
1087:            }
1088:
1089:            /**
1090:             * Attributes backed by attribute descriptors.
1091:             * ID/IDREFs not set.
1092:             */
1093:            private class ElementAttributes implements  Attributes {
1094:                /** Attribute descriptors backing the <code>Attributes</code> */
1095:                private AttributeDescriptor[] attributes;
1096:                /** Context to be evaluated when finding values */
1097:                private Context context;
1098:                /** Cached attribute values */
1099:                private String[] values;
1100:                /** The number of unsuppressed attributes */
1101:                private int length;
1102:
1103:                /** 
1104:                 * Construct attributes for element and context.
1105:                 *
1106:                 * @param descriptor the <code>ElementDescriptor</code> describing the element
1107:                 * @param context evaluate against this context
1108:                 */
1109:                ElementAttributes(ElementDescriptor descriptor, Context context) {
1110:                    this .context = context;
1111:                    init(descriptor.getAttributeDescriptors());
1112:                }
1113:
1114:                private void init(AttributeDescriptor[] baseAttributes) {
1115:                    attributes = new AttributeDescriptor[baseAttributes.length];
1116:                    values = new String[baseAttributes.length];
1117:                    int index = 0;
1118:                    for (int i = 0, size = baseAttributes.length; i < size; i++) {
1119:                        AttributeDescriptor baseAttribute = baseAttributes[i];
1120:                        String attributeValue = valueAttribute(baseAttribute);
1121:                        if (attributeValue != null
1122:                                && !context.getValueSuppressionStrategy()
1123:                                        .suppressAttribute(baseAttribute,
1124:                                                attributeValue)) {
1125:                            values[index] = attributeValue;
1126:                            attributes[index] = baseAttribute;
1127:                            index++;
1128:                        }
1129:                    }
1130:                    length = index;
1131:                }
1132:
1133:                private String valueAttribute(AttributeDescriptor attribute) {
1134:                    Expression expression = attribute.getTextExpression();
1135:                    if (expression != null) {
1136:                        Object value = expression.evaluate(context);
1137:                        return convertToString(value, attribute, context);
1138:                    }
1139:
1140:                    return "";
1141:                }
1142:
1143:                /**
1144:                 * Gets the index of an attribute by qualified name.
1145:                 * 
1146:                 * @param qName the qualified name of the attribute
1147:                 * @return the index of the attribute - or -1 if there is no matching attribute
1148:                 */
1149:                public int getIndex(String qName) {
1150:                    for (int i = 0; i < attributes.length; i++) {
1151:                        if (attributes[i].getQualifiedName() != null
1152:                                && attributes[i].getQualifiedName().equals(
1153:                                        qName)) {
1154:                            return i;
1155:                        }
1156:                    }
1157:                    return -1;
1158:                }
1159:
1160:                /**
1161:                 * Gets the index of an attribute by namespace name.
1162:                 *
1163:                 * @param uri the namespace uri of the attribute
1164:                 * @param localName the local name of the attribute
1165:                 * @return the index of the attribute - or -1 if there is no matching attribute
1166:                 */
1167:                public int getIndex(String uri, String localName) {
1168:                    for (int i = 0; i < attributes.length; i++) {
1169:                        if (attributes[i].getURI() != null
1170:                                && attributes[i].getURI().equals(uri)
1171:                                && attributes[i].getLocalName() != null
1172:                                && attributes[i].getURI().equals(localName)) {
1173:                            return i;
1174:                        }
1175:                    }
1176:
1177:                    return -1;
1178:                }
1179:
1180:                /**
1181:                 * Gets the number of attributes in the list.
1182:                 *
1183:                 * @return the number of attributes in this list
1184:                 */
1185:                public int getLength() {
1186:                    return length;
1187:                }
1188:
1189:                /** 
1190:                 * Gets the local name by index.
1191:                 * 
1192:                 * @param index the attribute index (zero based)
1193:                 * @return the attribute local name - or null if the index is out of range
1194:                 */
1195:                public String getLocalName(int index) {
1196:                    if (indexInRange(index)) {
1197:                        return attributes[index].getLocalName();
1198:                    }
1199:
1200:                    return null;
1201:                }
1202:
1203:                /**
1204:                 * Gets the qualified name by index.
1205:                 *
1206:                 * @param index the attribute index (zero based)
1207:                 * @return the qualified name of the element - or null if the index is our of range
1208:                 */
1209:                public String getQName(int index) {
1210:                    if (indexInRange(index)) {
1211:                        return attributes[index].getQualifiedName();
1212:                    }
1213:
1214:                    return null;
1215:                }
1216:
1217:                /**
1218:                 * Gets the attribute SAX type by namespace name.
1219:                 *
1220:                 * @param index the attribute index (zero based)
1221:                 * @return the attribute type (as a string) or null if the index is out of range
1222:                 */
1223:                public String getType(int index) {
1224:                    if (indexInRange(index)) {
1225:                        return "CDATA";
1226:                    }
1227:                    return null;
1228:                }
1229:
1230:                /**
1231:                 * Gets the attribute SAX type by qualified name.
1232:                 *
1233:                 * @param qName the qualified name of the attribute
1234:                 * @return the attribute type (as a string) or null if the attribute is not in the list
1235:                 */
1236:                public String getType(String qName) {
1237:                    return getType(getIndex(qName));
1238:                }
1239:
1240:                /**
1241:                 * Gets the attribute SAX type by namespace name.
1242:                 *
1243:                 * @param uri the namespace uri of the attribute
1244:                 * @param localName the local name of the attribute
1245:                 * @return the attribute type (as a string) or null if the attribute is not in the list
1246:                 */
1247:                public String getType(String uri, String localName) {
1248:                    return getType(getIndex(uri, localName));
1249:                }
1250:
1251:                /**
1252:                 * Gets the namespace URI for attribute at the given index.
1253:                 *
1254:                 * @param index the attribute index (zero-based)
1255:                 * @return the namespace URI (empty string if none is available) 
1256:                 * or null if the index is out of range
1257:                 */
1258:                public String getURI(int index) {
1259:                    if (indexInRange(index)) {
1260:                        return attributes[index].getURI();
1261:                    }
1262:                    return null;
1263:                }
1264:
1265:                /**
1266:                 * Gets the value for the attribute at given index.
1267:                 * 
1268:                 * @param index the attribute index (zero based)
1269:                 * @return the attribute value or null if the index is out of range
1270:                 * @todo add value caching
1271:                 */
1272:                public String getValue(int index) {
1273:                    if (indexInRange(index)) {
1274:                        return values[index];
1275:                    }
1276:                    return null;
1277:                }
1278:
1279:                /**
1280:                 * Gets the value for the attribute by qualified name.
1281:                 * 
1282:                 * @param qName the qualified name 
1283:                 * @return the attribute value or null if there are no attributes 
1284:                 * with the given qualified name
1285:                 * @todo add value caching
1286:                 */
1287:                public String getValue(String qName) {
1288:                    return getValue(getIndex(qName));
1289:                }
1290:
1291:                /**
1292:                 * Gets the value for the attribute by namespace name.
1293:                 * 
1294:                 * @param uri the namespace URI of the attribute
1295:                 * @param localName the local name of the attribute
1296:                 * @return the attribute value or null if there are not attributes 
1297:                 * with the given namespace and local name
1298:                 * @todo add value caching
1299:                 */
1300:                public String getValue(String uri, String localName) {
1301:                    return getValue(getIndex(uri, localName));
1302:                }
1303:
1304:                /**
1305:                 * Is the given index within the range of the attribute list
1306:                 *
1307:                 * @param index the index whose range will be checked
1308:                 * @return true if the index with within the range of the attribute list
1309:                 */
1310:                private boolean indexInRange(int index) {
1311:                    return (index >= 0 && index < getLength());
1312:                }
1313:            }
1314:
1315:            /**
1316:             * Attributes with generate ID/IDREF attributes
1317:             * //TODO: refactor the ID/REF generation so that it's fixed at introspection
1318:             * and the generators are placed into the Context.
1319:             * @author <a href='http://jakarta.apache.org/'>Jakarta Commons Team</a>
1320:             * @version $Revision: 438373 $
1321:             */
1322:            private class IDElementAttributes extends ElementAttributes {
1323:                /** ID attribute value */
1324:                private String idValue;
1325:                /** ID attribute name */
1326:                private String idAttributeName;
1327:
1328:                private boolean matchingAttribute = false;
1329:                private int length;
1330:                private int idIndex;
1331:
1332:                /** 
1333:                 * Construct attributes for element and context.
1334:                 *
1335:                 * @param descriptor the <code>ElementDescriptor</code> describing the element
1336:                 * @param context evaluate against this context
1337:                 * @param idAttributeName the name of the id attribute 
1338:                 * @param idValue the ID attribute value
1339:                 */
1340:                IDElementAttributes(ElementDescriptor descriptor,
1341:                        Context context, String idAttributeName, String idValue) {
1342:                    super (descriptor, context);
1343:                    this .idValue = idValue;
1344:                    this .idAttributeName = idAttributeName;
1345:
1346:                    // see if we have already have a matching attribute descriptor
1347:                    AttributeDescriptor[] attributeDescriptors = descriptor
1348:                            .getAttributeDescriptors();
1349:                    length = super .getLength();
1350:                    for (int i = 0; i < length; i++) {
1351:                        if (idAttributeName.equals(attributeDescriptors[i]
1352:                                .getQualifiedName())) {
1353:                            matchingAttribute = true;
1354:                            idIndex = i;
1355:                            break;
1356:                        }
1357:                    }
1358:                    if (!matchingAttribute) {
1359:                        length += 1;
1360:                        idIndex = length - 1;
1361:                    }
1362:                }
1363:
1364:                public int getIndex(String uri, String localName) {
1365:                    if (localName.equals(idAttributeName)) {
1366:                        return idIndex;
1367:                    }
1368:
1369:                    return super .getIndex(uri, localName);
1370:                }
1371:
1372:                public int getIndex(String qName) {
1373:                    if (qName.equals(idAttributeName)) {
1374:                        return idIndex;
1375:                    }
1376:
1377:                    return super .getIndex(qName);
1378:                }
1379:
1380:                public int getLength() {
1381:                    return length;
1382:                }
1383:
1384:                public String getLocalName(int index) {
1385:                    if (index == idIndex) {
1386:                        return idAttributeName;
1387:                    }
1388:                    return super .getLocalName(index);
1389:                }
1390:
1391:                public String getQName(int index) {
1392:                    if (index == idIndex) {
1393:                        return idAttributeName;
1394:                    }
1395:                    return super .getQName(index);
1396:                }
1397:
1398:                public String getType(int index) {
1399:                    if (index == idIndex) {
1400:                        return "ID";
1401:                    }
1402:                    return super .getType(index);
1403:                }
1404:
1405:                public String getType(String uri, String localName) {
1406:                    return getType(getIndex(uri, localName));
1407:                }
1408:
1409:                public String getType(String qName) {
1410:                    return getType(getIndex(qName));
1411:                }
1412:
1413:                public String getURI(int index) {
1414:                    //TODO: this is probably wrong
1415:                    // probably need to move ID management into introspection
1416:                    // before we can handle this namespace bit correctly
1417:                    if (index == idIndex) {
1418:                        return "";
1419:                    }
1420:                    return super .getURI(index);
1421:                }
1422:
1423:                public String getValue(int index) {
1424:                    if (index == idIndex) {
1425:                        return idValue;
1426:                    }
1427:                    return super .getValue(index);
1428:                }
1429:
1430:                public String getValue(String uri, String localName) {
1431:                    return getValue(getIndex(uri, localName));
1432:                }
1433:
1434:                public String getValue(String qName) {
1435:                    return getValue(getIndex(qName));
1436:                }
1437:
1438:            }
1439:
1440:            // OLD API (DEPRECATED)
1441:            // --------------------------------------------------------------------------------------
1442:
1443:            /** 
1444:             * Get the indentation for the current element. 
1445:             * Used for pretty priting.
1446:             *
1447:             * @return the amount that the current element is indented
1448:             * @deprecated 0.5 replaced by new SAX inspired API
1449:             */
1450:            protected int getIndentLevel() {
1451:                return 0;
1452:            }
1453:
1454:            // Expression methods
1455:            //-------------------------------------------------------------------------    
1456:
1457:            /** 
1458:             * Express an element tag start using given qualified name.
1459:             *
1460:             * @param qualifiedName the qualified name of the element to be expressed
1461:             * @throws IOException if an IO problem occurs during writing
1462:             * @throws SAXException if an SAX problem occurs during writing 
1463:             * @deprecated 0.5 replaced by new SAX inspired API
1464:             */
1465:            protected void expressElementStart(String qualifiedName)
1466:                    throws IOException, SAXException {
1467:                // do nothing
1468:            }
1469:
1470:            /** 
1471:             * Express an element tag start using given qualified name.
1472:             *
1473:             * @param uri the namespace uri 
1474:             * @param localName the local name for this element
1475:             * @param qualifiedName the qualified name of the element to be expressed
1476:             * @throws IOException if an IO problem occurs during writing
1477:             * @throws SAXException if an SAX problem occurs during writing 
1478:             * @deprecated 0.5 replaced by new SAX inspired API
1479:             */
1480:            protected void expressElementStart(String uri, String localName,
1481:                    String qualifiedName) throws IOException, SAXException {
1482:                expressElementStart(qualifiedName);
1483:            }
1484:
1485:            /**
1486:             * Express a closing tag.
1487:             *
1488:             * @throws IOException if an IO problem occurs during writing
1489:             * @throws SAXException if an SAX problem occurs during writing 
1490:             * @deprecated 0.5 replaced by new SAX inspired API
1491:             */
1492:            protected void expressTagClose() throws IOException, SAXException {
1493:            }
1494:
1495:            /** 
1496:             * Express an element end tag (with given name) 
1497:             *
1498:             * @param qualifiedName the qualified name for the element to be closed
1499:             *
1500:             * @throws IOException if an IO problem occurs during writing
1501:             * @throws SAXException if an SAX problem occurs during writing
1502:             * @deprecated 0.5 replaced by new SAX inspired API
1503:             */
1504:            protected void expressElementEnd(String qualifiedName)
1505:                    throws IOException, SAXException {
1506:                // do nothing
1507:            }
1508:
1509:            /** 
1510:             * Express an element end tag (with given name) 
1511:             *
1512:             * @param uri the namespace uri of the element close tag
1513:             * @param localName the local name of the element close tag
1514:             * @param qualifiedName the qualified name for the element to be closed
1515:             *
1516:             * @throws IOException if an IO problem occurs during writing
1517:             * @throws SAXException if an SAX problem occurs during writing
1518:             * @deprecated 0.5 replaced by new SAX inspired API
1519:             */
1520:            protected void expressElementEnd(String uri, String localName,
1521:                    String qualifiedName) throws IOException, SAXException {
1522:                expressElementEnd(qualifiedName);
1523:            }
1524:
1525:            /** 
1526:             * Express an empty element end.
1527:             * 
1528:             * @throws IOException if an IO problem occurs during writing
1529:             * @throws SAXException if an SAX problem occurs during writing
1530:             * @deprecated 0.5 replaced by new SAX inspired API
1531:             */
1532:            protected void expressElementEnd() throws IOException, SAXException {
1533:            }
1534:
1535:            /** 
1536:             * Express body text 
1537:             *
1538:             * @param text the string to write out as the body of the current element
1539:             * 
1540:             * @throws IOException if an IO problem occurs during writing
1541:             * @throws SAXException if an SAX problem occurs during writing
1542:             * @deprecated 0.5 replaced by new SAX inspired API
1543:             */
1544:            protected void expressBodyText(String text) throws IOException,
1545:                    SAXException {
1546:            }
1547:
1548:            /** 
1549:             * Express an attribute 
1550:             *
1551:             * @param qualifiedName the qualified name of the attribute
1552:             * @param value the attribute value
1553:             * @throws IOException if an IO problem occurs during writing
1554:             * @throws SAXException if an SAX problem occurs during writing
1555:             * @deprecated 0.5 replaced by new SAX inspired API
1556:             */
1557:            protected void expressAttribute(String qualifiedName, String value)
1558:                    throws IOException, SAXException {
1559:                // Do nothing
1560:            }
1561:
1562:            /** 
1563:             * Express an attribute 
1564:             *
1565:             * @param namespaceUri the namespace uri
1566:             * @param localName the local name
1567:             * @param qualifiedName the qualified name of the attribute
1568:             * @param value the attribute value
1569:             * @throws IOException if an IO problem occurs during writing
1570:             * @throws SAXException if an SAX problem occurs during writing
1571:             * @deprecated 0.5 replaced by new SAX inspired API
1572:             */
1573:            protected void expressAttribute(String namespaceUri,
1574:                    String localName, String qualifiedName, String value)
1575:                    throws IOException, SAXException {
1576:                expressAttribute(qualifiedName, value);
1577:            }
1578:
1579:            /** 
1580:             * Writes the given element 
1581:             *
1582:             * @param qualifiedName qualified name to use for the element
1583:             * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
1584:             * @param context the <code>Context</code> to use to evaluate the bean expressions
1585:             * @throws IOException if an IO problem occurs during writing
1586:             * @throws SAXException if an SAX problem occurs during writing 
1587:             * @throws IntrospectionException if a java beans introspection problem occurs
1588:             * @deprecated 0.5 replaced by new SAX inspired API
1589:             */
1590:            protected void write(String qualifiedName,
1591:                    ElementDescriptor elementDescriptor, Context context)
1592:                    throws IOException, SAXException, IntrospectionException {
1593:                writeElement("", qualifiedName, qualifiedName,
1594:                        elementDescriptor, context);
1595:            }
1596:
1597:            /** 
1598:             * Writes the given element adding an ID attribute 
1599:             *
1600:             * @param qualifiedName qualified name to use for the element
1601:             * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
1602:             * @param context the <code>Context</code> to use to evaluate the bean expressions
1603:             * @param idAttribute the qualified name of the <code>ID</code> attribute 
1604:             * @param idValue the value for the <code>ID</code> attribute 
1605:             * @throws IOException if an IO problem occurs during writing
1606:             * @throws SAXException if an SAX problem occurs during writing 
1607:             * @throws IntrospectionException if a java beans introspection problem occurs
1608:             * @deprecated 0.5 replaced by new SAX inspired API
1609:             */
1610:            protected void write(String qualifiedName,
1611:                    ElementDescriptor elementDescriptor, Context context,
1612:                    String idAttribute, String idValue) throws IOException,
1613:                    SAXException, IntrospectionException {
1614:                writeElement("", qualifiedName, qualifiedName,
1615:                        elementDescriptor, context, idAttribute, idValue);
1616:            }
1617:
1618:            /** 
1619:             * Write attributes, child elements and element end 
1620:             *
1621:             * @param qualifiedName qualified name to use for the element
1622:             * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
1623:             * @param context the <code>Context</code> to use to evaluate the bean expressions
1624:             * @throws IOException if an IO problem occurs during writing
1625:             * @throws SAXException if an SAX problem occurs during writing 
1626:             * @throws IntrospectionException if a java beans introspection problem occurs
1627:             * @deprecated 0.5 replaced by new SAX inspired API
1628:             */
1629:            protected void writeRestOfElement(String qualifiedName,
1630:                    ElementDescriptor elementDescriptor, Context context)
1631:                    throws IOException, SAXException, IntrospectionException {
1632:                writeRestOfElement("", qualifiedName, qualifiedName,
1633:                        elementDescriptor, context);
1634:            }
1635:
1636:            /**
1637:             * Writes an element with a <code>IDREF</code> attribute 
1638:             *
1639:             * @param qualifiedName of the element with <code>IDREF</code> attribute 
1640:             * @param idrefAttributeName the qualified name of the <code>IDREF</code> attribute 
1641:             * @param idrefAttributeValue the value for the <code>IDREF</code> attribute 
1642:             * @throws IOException if an IO problem occurs during writing
1643:             * @throws SAXException if an SAX problem occurs during writing 
1644:             * @throws IntrospectionException if a java beans introspection problem occurs
1645:             * @deprecated 0.5 replaced by new SAX inspired API
1646:             */
1647:            protected void writeIDREFElement(String qualifiedName,
1648:                    String idrefAttributeName, String idrefAttributeValue)
1649:                    throws IOException, SAXException, IntrospectionException {
1650:                // deprecated
1651:                AttributesImpl attributes = new AttributesImpl();
1652:                attributes.addAttribute("", idrefAttributeName,
1653:                        idrefAttributeName, "IDREF", idrefAttributeValue);
1654:                startElement("", qualifiedName, qualifiedName, attributes);
1655:                endElement("", qualifiedName, qualifiedName);
1656:            }
1657:
1658:            /** 
1659:             * Writes the element content.
1660:             *
1661:             * @param elementDescriptor the <code>ElementDescriptor</code> to write as xml 
1662:             * @param context the <code>Context</code> to use to evaluate the bean expressions
1663:             * @return true if some content was written
1664:             * @throws IOException if an IO problem occurs during writing
1665:             * @throws SAXException if an SAX problem occurs during writing 
1666:             * @throws IntrospectionException if a java beans introspection problem occurs
1667:             * @deprecated 0.5 replaced by new SAX inspired API
1668:             */
1669:            protected boolean writeContent(ElementDescriptor elementDescriptor,
1670:                    Context context) throws IOException, SAXException,
1671:                    IntrospectionException {
1672:                return false;
1673:            }
1674:
1675:            /**  
1676:             * Writes the attribute declarations 
1677:             *
1678:             * @param elementDescriptor the <code>ElementDescriptor</code> to be written out as xml
1679:             * @param context the <code>Context</code> to use to evaluation bean expressions
1680:             * @throws IOException if an IO problem occurs during writing
1681:             * @throws SAXException if an SAX problem occurs during writing 
1682:             * @deprecated 0.5 replaced by new SAX inspired API
1683:             */
1684:            protected void writeAttributes(ElementDescriptor elementDescriptor,
1685:                    Context context) throws IOException, SAXException {
1686:                if (!elementDescriptor.isWrapCollectionsInElement()) {
1687:                    return;
1688:                }
1689:
1690:                AttributeDescriptor[] attributeDescriptors = elementDescriptor
1691:                        .getAttributeDescriptors();
1692:                if (attributeDescriptors != null) {
1693:                    for (int i = 0, size = attributeDescriptors.length; i < size; i++) {
1694:                        AttributeDescriptor attributeDescriptor = attributeDescriptors[i];
1695:                        writeAttribute(attributeDescriptor, context);
1696:                    }
1697:                }
1698:            }
1699:
1700:            /** 
1701:             * Writes an attribute declaration 
1702:             *
1703:             * @param attributeDescriptor the <code>AttributeDescriptor</code> to be written as xml
1704:             * @param context the <code>Context</code> to use to evaluation bean expressions
1705:             * @throws IOException if an IO problem occurs during writing
1706:             * @throws SAXException if an SAX problem occurs during writing 
1707:             * @deprecated 0.5 replaced by new SAX inspired API
1708:             */
1709:            protected void writeAttribute(
1710:                    AttributeDescriptor attributeDescriptor, Context context)
1711:                    throws IOException, SAXException {
1712:                Expression expression = attributeDescriptor.getTextExpression();
1713:                if (expression != null) {
1714:                    Object value = expression.evaluate(context);
1715:                    if (value != null) {
1716:                        String text = value.toString();
1717:                        if (text != null && text.length() > 0) {
1718:                            expressAttribute(attributeDescriptor.getURI(),
1719:                                    attributeDescriptor.getLocalName(),
1720:                                    attributeDescriptor.getQualifiedName(),
1721:                                    text);
1722:                        }
1723:                    }
1724:                }
1725:            }
1726:
1727:            /** 
1728:             * Writes a empty line.  
1729:             * This implementation does nothing but can be overridden by subclasses.
1730:             *
1731:             * @throws IOException if the line cannot be written
1732:             * @deprecated 0.5 replaced by new SAX inspired API
1733:             */
1734:            protected void writePrintln() throws IOException {
1735:            }
1736:
1737:            /** 
1738:             * Writes an indentation.
1739:             * This implementation does nothing but can be overridden by subclasses.
1740:             * 
1741:             * @throws IOException if the indent cannot be written
1742:             * @deprecated 0.5 replaced by new BeanWriter API
1743:             */
1744:            protected void writeIndent() throws IOException {
1745:            }
1746:
1747:            /**
1748:             * Converts an object to a string.
1749:             *
1750:             * @param value the Object to represent as a String, possibly null
1751:             * @param descriptor writing out this descriptor not null
1752:             * @param context not null
1753:             * @return String representation, not null
1754:             */
1755:            private String convertToString(Object value, Descriptor descriptor,
1756:                    Context context) {
1757:                return getBindingConfiguration().getObjectStringConverter()
1758:                        .objectToString(value, descriptor.getPropertyType(),
1759:                                context);
1760:            }
1761:
1762:            /**
1763:             * Factory method for new contexts.
1764:             * Ensure that they are correctly configured.
1765:             * @param bean make a new Context for this bean
1766:             * @return not null
1767:             */
1768:            private Context makeContext(Object bean) {
1769:                return new Context(bean, log, bindingConfiguration);
1770:            }
1771:
1772:            /**
1773:             * Basic mutable implementation of <code>WriteContext</code>.
1774:             */
1775:            private static class WriteContextImpl extends WriteContext {
1776:
1777:                private ElementDescriptor currentDescriptor;
1778:
1779:                /**
1780:                 * @see org.apache.commons.betwixt.io.WriteContext#getCurrentDescriptor()
1781:                 */
1782:                public ElementDescriptor getCurrentDescriptor() {
1783:                    return currentDescriptor;
1784:                }
1785:
1786:                /**
1787:                 * Sets the descriptor for the current element.
1788:                 * @param currentDescriptor
1789:                 */
1790:                public void setCurrentDescriptor(
1791:                        ElementDescriptor currentDescriptor) {
1792:                    this.currentDescriptor = currentDescriptor;
1793:                }
1794:
1795:            }
1796:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.