1
/* Copyright 2003, 2004 Elliotte Rusty Harold
3
This library is free software; you can redistribute it and/or modify
4
it under the terms of version 2.1 of the GNU Lesser General Public
5
License as published by the Free Software Foundation.
7
This library is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU Lesser General Public License for more details.
12
You should have received a copy of the GNU Lesser General Public
13
License along with this library; if not, write to the
14
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
15
Boston, MA 02111-1307 USA
17
You can contact Elliotte Rusty Harold by sending e-mail to
18
elharo@metalab.unc.edu. Please include the word "XOM" in the
19
subject line. The XOM home page is located at http://www.xom.nu/
22
package nu.xom.samples;
24
import java.io.IOException;
25
import java.io.OutputStream;
26
import java.io.UnsupportedEncodingException;
28
import nu.xom.Attribute;
29
import nu.xom.Builder;
30
import nu.xom.DocType;
31
import nu.xom.Document;
32
import nu.xom.Element;
33
import nu.xom.ParsingException;
34
import nu.xom.ProcessingInstruction;
35
import nu.xom.Serializer;
39
* This class writes XML with a maximum line length,
40
* but only breaks lines inside tags. It does
41
* not change a document's infoset.
44
* @author Elliotte Rusty Harold
48
public class WrappingSerializer extends Serializer {
50
public WrappingSerializer(OutputStream out) {
54
public WrappingSerializer(OutputStream out, String encoding)
55
throws UnsupportedEncodingException {
59
private int maxLength;
63
* Returns the preferred maximum line length.
66
* @return the maximum line length.
68
public int getMaxLength() {
69
return this.maxLength;
74
* Sets the suggested maximum line length for this serializer.
75
* Setting this to 0 indicates that no automatic wrapping is to be
76
* performed. When a line approaches this length, the serializer
77
* begins looking for opportunities to break the line.
78
* It will only break inside a tag, at places that do not
79
* affect the infoset, such as between attribute values or
80
* before the closing <code>></code>. In some circumstances the
81
* serializer may not be able to break the line before the maximum
82
* length is reached. In this case,
83
* the serializer will exceed the maximum line length.
87
* The default value for maxLength is 0, which is
88
* interpreted as no maximum line length.
89
* Setting this to a negative value just sets it to 0.
92
* @param maxLength the suggested maximum line length
94
public void setMaxLength(int maxLength) {
95
this.maxLength = maxLength;
98
protected void writeStartTag(Element element)
101
writeRaw(element.getQualifiedName());
102
writeAttributes(element);
103
writeNamespaceDeclarations(element);
104
if (needsBreak()) breakLine();
108
protected void writeEmptyElementTag(Element element)
111
writeRaw(element.getQualifiedName());
112
writeAttributes(element);
113
writeNamespaceDeclarations(element);
114
if (needsBreak()) breakLine();
118
public void writeEndTag(Element element) throws IOException {
120
writeRaw(element.getQualifiedName());
121
if (needsBreak()) breakLine();
127
* This method writes an attribute in the form
128
* <code><i>name</i>="<i>value</i>"</code>.
129
* Characters in the attribute value are escaped as necessary.
132
* @param attribute the <code>Attribute</code> to write
134
* @throws IOException if the underlying <code>OutputStream</code>
135
* encounters an I/O error
137
protected void write(Attribute attribute) throws IOException {
139
String name = attribute.getQualifiedName();
140
if (maxLength <= this.getColumnNumber() + name.length()) {
144
if (this.getColumnNumber() == maxLength) {
148
String value = attribute.getValue();
149
if (maxLength < value.length() + 2) {
153
writeAttributeValue(attribute.getValue());
160
* This writes a namespace declaration in the form
161
* <code>xmlns:<i>prefix</i>="<i>uri</i>"</code> or
162
* <code>xmlns="<i>uri</i>"</code>.
165
* @param prefix the namespace prefix; the empty string for the
167
* @param uri the namespace URI
169
* @throws IOException if the underlying <code>OutputStream</code>
170
* encounters an I/O error
172
protected void writeNamespaceDeclaration(String prefix, String uri)
176
if ("".equals(prefix)) {
180
name = "xmlns:" + prefix;
182
if (this.maxLength < this.getColumnNumber() + name.length()) {
187
if (this.getColumnNumber() == maxLength) {
192
if (this.maxLength < this.getColumnNumber() + uri.length() + 2) {
200
private boolean needsBreak() {
202
return this.maxLength - this.getColumnNumber() <= 10;
209
* Serializes a <code>ProcessingInstruction</code> object
210
* onto the output stream. Line breaks may be inserted
211
* following the target.
215
* Since character and entity references are not resolved
216
* in processing instructions, processing instructions
217
* can only be serialized when all
218
* characters they contain are available in the current
222
* @param instruction the <code>ProcessingInstruction</code>
225
* @throws IOException if the underlying <code>OutputStream</code>
226
* encounters an I/O error
228
protected void write(ProcessingInstruction instruction)
231
writeRaw(instruction.getTarget());
232
String value = instruction.getValue();
233
if (maxLength < getColumnNumber() + value.length() + 3) {
245
* Serializes a <code>DocType</code> object
246
* onto the output stream.
249
* @param doctype the document type declaration to serialize
251
* @throws IOException if the underlying
252
* <code>OutputStream</code> encounters an I/O error
254
protected void write(DocType doctype) throws IOException {
255
writeRaw("<!DOCTYPE");
256
String rootElementName = doctype.getRootElementName();
257
if (maxLength < getColumnNumber() + rootElementName.length() + 1) {
263
writeRaw(rootElementName);
265
String publicID = doctype.getPublicID();
266
String systemID = doctype.getSystemID();
267
if (publicID != null) {
268
if (maxLength < getColumnNumber() + 6) {
276
if (maxLength < getColumnNumber() + publicID.length() + 2) {
286
if (maxLength < getColumnNumber() + systemID.length() + 2) {
296
else if (systemID != null) {
297
if (maxLength < getColumnNumber() + 6) {
305
if (maxLength < getColumnNumber() + systemID.length() + 2) {
316
String internalDTDSubset = doctype.getInternalDTDSubset();
317
if (!internalDTDSubset.equals("")) {
318
if (maxLength < getColumnNumber() + 2) {
324
writeRaw(internalDTDSubset);
329
if (maxLength < getColumnNumber() + 1) {
336
public static void main(String[] args) {
338
if (args.length <= 0) {
339
System.out.println("Usage: java nu.xom.samples.WrappingSerializer URL");
344
Builder parser = new Builder();
345
Document doc = parser.build(args[0]);
346
Serializer serializer = new WrappingSerializer(System.out, "ISO-8859-1");
347
serializer.setIndent(4);
348
serializer.setMaxLength(24);
349
serializer.setPreserveBaseURI(true);
350
serializer.write(doc);
353
catch (ParsingException ex) {
354
System.out.println(args[0] + " is not well-formed.");
355
System.out.println(ex.getMessage());
357
catch (IOException ex) {
359
"Due to an IOException, the parser could not read "