📄 abstractbeanwriter.java
字号:
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.betwixt.io;
import java.beans.IntrospectionException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import org.apache.commons.betwixt.AttributeDescriptor;
import org.apache.commons.betwixt.BindingConfiguration;
import org.apache.commons.betwixt.Descriptor;
import org.apache.commons.betwixt.ElementDescriptor;
import org.apache.commons.betwixt.XMLBeanInfo;
import org.apache.commons.betwixt.XMLIntrospector;
import org.apache.commons.betwixt.digester.XMLIntrospectorHelper;
import org.apache.commons.betwixt.expression.Context;
import org.apache.commons.betwixt.expression.Expression;
import org.apache.commons.betwixt.io.id.SequentialIDGenerator;
import org.apache.commons.collections.ArrayStack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
// FIX ME!!!
// Logging logic!
// FIX ME!!
// Error handling strategy!
// i'm going to add SAXExceptions everywhere since it's the easiest way to make things work quick
// but this is a poor strategy
/**
* <p>Abstract superclass for bean writers.
* This class encapsulates the processing logic.
* Subclasses provide implementations for the actual expression of the xml.</p>
* <h5>SAX Inspired Writing API</h5>
* <p>
* This class is intended to be used by subclassing:
* concrete subclasses perform the actual writing by providing
* suitable implementations for the following methods inspired
* by <a href='http://www.saxproject.org'>SAX</a>:
* </p>
* <ul>
* <li> {@link #start} - called when processing begins</li>
* <li> {@link #startElement(WriteContext, String, String, String, Attributes)}
* - called when the start of an element
* should be written</li>
* <li> {@link #bodyText(WriteContext, String)}
* - called when the start of an element
* should be written</li>
* <li> {@link #endElement(WriteContext, String, String, String)}
* - called when the end of an element
* should be written</li>
* <li> {@link #end} - called when processing has been completed</li>
* </ul>
* <p>
* <strong>Note</strong> that this class contains many deprecated
* versions of the writing API. These will be removed soon so care
* should be taken to use the latest version.
* </p>
* <p>
* <strong>Note</strong> that this class is designed to be used
* in a single threaded environment. When used in multi-threaded
* environments, use of a common <code>XMLIntrospector</code>
* and pooled writer instances should be considered.
* </p>
*
* @author <a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a>
*/
public abstract class AbstractBeanWriter {
/** Introspector used */
private XMLIntrospector introspector = new XMLIntrospector();
/** Log used for logging (Doh!) */
private Log log = LogFactory.getLog( AbstractBeanWriter.class );
/** Map containing ID attribute values for beans */
private HashMap idMap = new HashMap();
/** Stack containing beans - used to detect cycles */
private ArrayStack beanStack = new ArrayStack();
/** Used to generate ID attribute values*/
private IDGenerator idGenerator = new SequentialIDGenerator();
/** Should empty elements be written out? */
private boolean writeEmptyElements = true;
/** Dynamic binding configuration settings */
private BindingConfiguration bindingConfiguration = new BindingConfiguration();
/** <code>WriteContext</code> implementation reused curing writing */
private WriteContextImpl writeContext = new WriteContextImpl();
/** Collection of namespaces which have already been declared */
private Collection namespacesDeclared = new ArrayList();
/**
* Marks the start of the bean writing.
* By default doesn't do anything, but can be used
* to do extra start processing
* @throws IOException if an IO problem occurs during writing
* @throws SAXException if an SAX problem occurs during writing
*/
public void start() throws IOException, SAXException {
}
/**
* Marks the start of the bean writing.
* By default doesn't do anything, but can be used
* to do extra end processing
* @throws IOException if an IO problem occurs during writing
* @throws SAXException if an SAX problem occurs during writing
*/
public void end() throws IOException, SAXException {
}
/**
* <p> Writes the given bean to the current stream using the XML introspector.</p>
*
* <p> This writes an xml fragment representing the bean to the current stream.</p>
*
* <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
* is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
* setting of the </code>BindingConfiguration</code> is false.</p>
*
* @throws IOException if an IO problem occurs during writing
* @throws SAXException if an SAX problem occurs during writing
* @throws IntrospectionException if a java beans introspection problem occurs
*
* @param bean write out representation of this bean
*/
public void write(Object bean) throws
IOException,
SAXException,
IntrospectionException {
if (log.isDebugEnabled()) {
log.debug( "Writing bean graph..." );
log.debug( bean );
}
start();
writeBean( null, null, null, bean, makeContext( bean ) );
end();
if (log.isDebugEnabled()) {
log.debug( "Finished writing bean graph." );
}
}
/**
* <p>Writes the given bean to the current stream
* using the given <code>qualifiedName</code>.</p>
*
* <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
* is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
* setting of the <code>BindingConfiguration</code> is false.</p>
*
* @param qualifiedName the string naming root element
* @param bean the <code>Object</code> to write out as xml
*
* @throws IOException if an IO problem occurs during writing
* @throws SAXException if an SAX problem occurs during writing
* @throws IntrospectionException if a java beans introspection problem occurs
*/
public void write(
String qualifiedName,
Object bean)
throws
IOException,
SAXException,
IntrospectionException {
start();
writeBean( "", qualifiedName, qualifiedName, bean, makeContext( bean ) );
end();
}
/**
* <p>Writes the given bean to the current stream
* using the given <code>qualifiedName</code>.</p>
*
* <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
* is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
* setting of the <code>BindingConfiguration</code> is false.</p>
*
* @param namespaceUri the namespace uri
* @param localName the local name
* @param qualifiedName the string naming root element
* @param bean the <code>Object</code> to write out as xml
* @param context not null
*
* @throws IOException if an IO problem occurs during writing
* @throws SAXException if an SAX problem occurs during writing
* @throws IntrospectionException if a java beans introspection problem occurs
*/
private void writeBean (
String namespaceUri,
String localName,
String qualifiedName,
Object bean,
Context context)
throws
IOException,
SAXException,
IntrospectionException {
if ( log.isTraceEnabled() ) {
log.trace( "Writing bean graph (qualified name '" + qualifiedName + "'" );
}
// introspect to obtain bean info
XMLBeanInfo beanInfo = introspector.introspect( bean );
if ( beanInfo != null ) {
ElementDescriptor elementDescriptor = beanInfo.getElementDescriptor();
if ( elementDescriptor != null ) {
context = context.newContext( bean );
if ( qualifiedName == null ) {
qualifiedName = elementDescriptor.getQualifiedName();
}
if ( namespaceUri == null ) {
namespaceUri = elementDescriptor.getURI();
}
if ( localName == null ) {
localName = elementDescriptor.getLocalName();
}
String ref = null;
String id = null;
// simple type should not have IDs
if ( elementDescriptor.isSimple() ) {
// write without an id
writeElement(
namespaceUri,
localName,
qualifiedName,
elementDescriptor,
context );
} else {
pushBean ( context.getBean() );
if ( getBindingConfiguration().getMapIDs() ) {
ref = (String) idMap.get( context.getBean() );
}
if ( ref == null ) {
// this is the first time that this bean has be written
AttributeDescriptor idAttribute = beanInfo.getIDAttribute();
if (idAttribute == null) {
// use a generated id
id = idGenerator.nextId();
idMap.put( bean, id );
if ( getBindingConfiguration().getMapIDs() ) {
// write element with id
writeElement(
namespaceUri,
localName,
qualifiedName,
elementDescriptor,
context ,
beanInfo.getIDAttributeName(),
id);
} else {
// write element without ID
writeElement(
namespaceUri,
localName,
qualifiedName,
elementDescriptor,
context );
}
} else {
// use id from bean property
// it's up to the user to ensure uniqueness
// XXX should we trap nulls?
Object exp = idAttribute.getTextExpression().evaluate( context );
if (exp == null) {
// we'll use a random id
log.debug("Using random id");
id = idGenerator.nextId();
} else {
// convert to string
id = exp.toString();
}
idMap.put( bean, id);
// the ID attribute should be written automatically
writeElement(
namespaceUri,
localName,
qualifiedName,
elementDescriptor,
context );
}
} else {
if ( !ignoreElement( elementDescriptor, context )) {
// we've already written this bean so write an IDREF
writeIDREFElement(
elementDescriptor,
namespaceUri,
localName,
qualifiedName,
beanInfo.getIDREFAttributeName(),
ref);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -