📄 beanwriter.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.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import org.apache.commons.betwixt.XMLUtils;
import org.apache.commons.betwixt.strategy.MixedContentEncodingStrategy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
/** <p><code>BeanWriter</code> outputs beans as XML to an io stream.</p>
*
* <p>The output for each bean is an xml fragment
* (rather than a well-formed xml-document).
* This allows bean representations to be appended to a document
* by writing each in turn to the stream.
* So to create a well formed xml document,
* you'll need to write the prolog to the stream first.
* If you append more than one bean to the stream,
* then you'll need to add a wrapping root element as well.
*
* <p> The line ending to be used is set by {@link #setEndOfLine}.
*
* <p> The output can be formatted (with whitespace) for easy reading
* by calling {@link #enablePrettyPrint}.
* The output will be indented.
* The indent string used is set by {@link #setIndent}.
*
* <p> Bean graphs can sometimes contain cycles.
* Care must be taken when serializing cyclic bean graphs
* since this can lead to infinite recursion.
* The approach taken by <code>BeanWriter</code> is to automatically
* assign an <code>ID</code> attribute value to beans.
* When a cycle is encountered,
* an element is written that has the <code>IDREF</code> attribute set to the
* id assigned earlier.
*
* <p> The names of the <code>ID</code> and <code>IDREF</code> attributes used
* can be customized by the <code>XMLBeanInfo</code>.
* The id's used can also be customized by the user
* via <code>IDGenerator</code> subclasses.
* The implementation used can be set by the <code>IdGenerator</code> property.
* BeanWriter defaults to using <code>SequentialIDGenerator</code>
* which supplies id values in numeric sequence.
*
* <p>If generated <code>ID</code> attribute values are not acceptable in the output,
* then this can be disabled by setting the <code>WriteIDs</code> property to false.
* If a cyclic reference is encountered in this case then a
* <code>CyclicReferenceException</code> will be thrown.
* When the <code>WriteIDs</code> property is set to false,
* it is recommended that this exception is caught by the caller.
*
*
* @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
* @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a>
*/
public class BeanWriter extends AbstractBeanWriter {
/** Where the output goes */
private Writer writer;
/** text used for end of lines. Defaults to <code>\n</code>*/
private static final String EOL = "\n";
/** text used for end of lines. Defaults to <code>\n</code>*/
private String endOfLine = EOL;
/** indentation text */
private String indent;
/** should we flush after writing bean */
private boolean autoFlush;
/** Log used for logging (Doh!) */
private Log log = LogFactory.getLog( BeanWriter.class );
/** Has any content (excluding attributes) been written to the current element */
private boolean currentElementIsEmpty = false;
/** Has the current element written any body text */
private boolean currentElementHasBodyText = false;
/** Has the last start tag been closed */
private boolean closedStartTag = true;
/** Should an end tag be added for empty elements? */
private boolean addEndTagForEmptyElement = false;
/** Current level of indentation (starts at 1 with the first element) */
private int indentLevel;
/** USed to determine how body content should be encoded before being output*/
private MixedContentEncodingStrategy mixedContentEncodingStrategy
= MixedContentEncodingStrategy.DEFAULT;
/**
* <p> Constructor uses <code>System.out</code> for output.</p>
*/
public BeanWriter() {
this( System.out );
}
/**
* <p> Constuctor uses given <code>OutputStream</code> for output.</p>
*
* @param out write out representations to this stream
*/
public BeanWriter(OutputStream out) {
this.writer = new BufferedWriter( new OutputStreamWriter( out ) );
this.autoFlush = true;
}
/**
* <p>Constuctor uses given <code>OutputStream</code> for output
* and allows encoding to be set.</p>
*
* @param out write out representations to this stream
* @param enc the name of the encoding to be used. This should be compatible
* with the encoding types described in <code>java.io</code>
* @throws UnsupportedEncodingException if the given encoding is not supported
*/
public BeanWriter(OutputStream out, String enc) throws UnsupportedEncodingException {
this.writer = new BufferedWriter( new OutputStreamWriter( out, enc ) );
this.autoFlush = true;
}
/**
* <p> Constructor sets writer used for output.</p>
*
* @param writer write out representations to this writer
*/
public BeanWriter(Writer writer) {
this.writer = writer;
}
/**
* A helper method that allows you to write the XML Declaration.
* This should only be called once before you output any beans.
*
* @param xmlDeclaration is the XML declaration string typically of
* the form "<xml version='1.0' encoding='UTF-8' ?>
*
* @throws IOException when declaration cannot be written
*/
public void writeXmlDeclaration(String xmlDeclaration) throws IOException {
writer.write( xmlDeclaration );
printLine();
}
/**
* Allows output to be flushed on the underlying output stream
*
* @throws IOException when the flush cannot be completed
*/
public void flush() throws IOException {
writer.flush();
}
/**
* Closes the underlying output stream
*
* @throws IOException when writer cannot be closed
*/
public void close() throws IOException {
writer.close();
}
/**
* Write the given object to the stream (and then flush).
*
* @param bean write this <code>Object</code> to the stream
* @throws IOException if an IO problem causes failure
* @throws SAXException if a SAX problem causes failure
* @throws IntrospectionException if bean cannot be introspected
*/
public void write(Object bean) throws IOException, SAXException, IntrospectionException {
super.write(bean);
if ( autoFlush ) {
writer.flush();
}
}
/**
* <p> Switch on formatted output.
* This sets the end of line and the indent.
* The default is adding 2 spaces and a newline
*/
public void enablePrettyPrint() {
endOfLine = EOL;
indent = " ";
}
/**
* Gets the string used to mark end of lines.
*
* @return the string used for end of lines
*/
public String getEndOfLine() {
return endOfLine;
}
/**
* Sets the string used for end of lines
* Produces a warning the specified value contains an invalid whitespace character
*
* @param endOfLine the <code>String</code to use
*/
public void setEndOfLine(String endOfLine) {
this.endOfLine = endOfLine;
for (int i = 0; i < endOfLine.length(); i++) {
if (!Character.isWhitespace(endOfLine.charAt(i))) {
log.warn("Invalid EndOfLine character(s)");
break;
}
}
}
/**
* Gets the indent string
*
* @return the string used for indentation
*/
public String getIndent() {
return indent;
}
/**
* Sets the string used for pretty print indents
* @param indent use this <code>string</code> for indents
*/
public void setIndent(String indent) {
this.indent = indent;
}
/**
* <p> Set the log implementation used. </p>
*
* @return a <code>org.apache.commons.logging.Log</code> level constant
*/
public Log getLog() {
return log;
}
/**
* <p> Set the log implementation used. </p>
*
* @param log <code>Log</code> implementation to use
*/
public void setLog( Log log ) {
this.log = log;
}
/**
* Gets the encoding strategy for mixed content.
* This is used to process body content
* before it is written to the textual output.
* @return the <code>MixedContentEncodingStrategy</code>, not null
* @since 0.5
*/
public MixedContentEncodingStrategy getMixedContentEncodingStrategy() {
return mixedContentEncodingStrategy;
}
/**
* Sets the encoding strategy for mixed content.
* This is used to process body content
* before it is written to the textual output.
* @param strategy the <code>MixedContentEncodingStrategy</code>
* used to process body content, not null
* @since 0.5
*/
public void setMixedContentEncodingStrategy(MixedContentEncodingStrategy strategy) {
mixedContentEncodingStrategy = strategy;
}
/**
* Should an end tag be added for each empty element?
* When this property is false then empty elements will
* be written as <code><<em>element-name</em>/gt;</code>.
* When this property is true then empty elements will
* be written as <code><<em>element-name</em>gt;
* </<em>element-name</em>gt;</code>.
* @return true if an end tag should be added
*/
public boolean isEndTagForEmptyElement() {
return addEndTagForEmptyElement;
}
/**
* Sets when an an end tag be added for each empty element?
* When this property is false then empty elements will
* be written as <code><<em>element-name</em>/gt;</code>.
* When this property is true then empty elements will
* be written as <code><<em>element-name</em>gt;
* </<em>element-name</em>gt;</code>.
* @param addEndTagForEmptyElement true if an end tag should be
* written for each empty element, false otherwise
*/
public void setEndTagForEmptyElement(boolean addEndTagForEmptyElement) {
this.addEndTagForEmptyElement = addEndTagForEmptyElement;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -