Class | Line # | Actions | |||||
---|---|---|---|---|---|---|---|
XMLWriter | 52 | 28 | 0% | 14 | 12 |
1 | /* | |
2 | * See the NOTICE file distributed with this work for additional | |
3 | * information regarding copyright ownership. | |
4 | * | |
5 | * This is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU Lesser General Public License as | |
7 | * published by the Free Software Foundation; either version 2.1 of | |
8 | * the License, or (at your option) any later version. | |
9 | * | |
10 | * This software is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * Lesser General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU Lesser General Public | |
16 | * License along with this software; if not, write to the Free | |
17 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
18 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. | |
19 | */ | |
20 | package com.xpn.xwiki.internal.xml; | |
21 | ||
22 | import java.io.IOException; | |
23 | import java.io.InputStream; | |
24 | import java.io.OutputStream; | |
25 | import java.io.Reader; | |
26 | import java.io.UnsupportedEncodingException; | |
27 | import java.util.Stack; | |
28 | ||
29 | import org.apache.commons.codec.binary.Base64OutputStream; | |
30 | import org.apache.commons.io.IOUtils; | |
31 | import org.apache.commons.io.output.CloseShieldOutputStream; | |
32 | import org.dom4j.Document; | |
33 | import org.dom4j.Element; | |
34 | import org.dom4j.io.OutputFormat; | |
35 | ||
36 | /** | |
37 | * Extension to <code>{@link org.dom4j.io.XMLWriter}</code> to allow filling some element content with an input stream, | |
38 | * minimizing the memory footprint of the operation. | |
39 | * <p> | |
40 | * This extension is not intended to be used to format a DOM4J tree to a stream, but to immediately write out the tags | |
41 | * produced without building the document tree in memory. It is not compatible with the SAX part of the original | |
42 | * <code>{@link org.dom4j.io.XMLWriter}</code>. | |
43 | * </p> | |
44 | * <p> | |
45 | * An improvement to the writeOpen/writeClose functions ensure better handling of independent opening and closing of | |
46 | * tags by maintaining a state stack of opened tags. New writeDocumentStart/End function also ensure proper starting and | |
47 | * ending of the document it self. | |
48 | * </p> | |
49 | * | |
50 | * @version $Id: cb5a6e08619da9ffe54b8833cdaa0dcd887c770b $ | |
51 | */ | |
52 | public class XMLWriter extends org.dom4j.io.XMLWriter | |
53 | { | |
54 | /** | |
55 | * <code>{@link Stack}</code> of currently opened <code>{@link Element}</code>, the first | |
56 | * <code>{@link Element}</code> is the document root element, and the top of the stack is the last opened | |
57 | * <code>{@link Element}</code>. | |
58 | */ | |
59 | protected Stack<Element> parent = new Stack<Element>(); | |
60 | ||
61 | /** | |
62 | * Current <code>{@link OutputStream}</code> of this writer. | |
63 | */ | |
64 | private OutputStream out; | |
65 | ||
66 | /** | |
67 | * Default constructor used by <code>{@link DOMXMLWriter}</code>. | |
68 | * | |
69 | * @see DOMXMLWriter | |
70 | */ | |
71 | 3 | protected XMLWriter() |
72 | { | |
73 | } | |
74 | ||
75 | /** | |
76 | * Create a new XMLWriter writing to a provided OutputStream in a given format. Note that other constructor of the | |
77 | * original DOM4J XMLWriter are unsupported since a OutputStream is the only way we can support the extensions | |
78 | * provided here. | |
79 | * <p> | |
80 | * Note that the writer is buffered and only a call to flush() or writeDocuemntEnd() will ensure the output has been | |
81 | * fully written to the <code>{@link OutputStream}</code>. | |
82 | * </p> | |
83 | * | |
84 | * @param out an <code>{@link OutputStream}</code> where to output the XML produced. | |
85 | * @param format an <code>{@link OutputFormat}</code> defining the encoding that should be used and esthetics of the | |
86 | * produced XML. | |
87 | * @throws UnsupportedEncodingException the requested encoding is unsupported. | |
88 | */ | |
89 | 63 | public XMLWriter(OutputStream out, OutputFormat format) throws UnsupportedEncodingException |
90 | { | |
91 | 63 | super(out, format); |
92 | 63 | this.out = out; |
93 | } | |
94 | ||
95 | /** | |
96 | * Write the <code>{@link Document}</code> declaration, and its <code>{@link org.dom4j.DocumentType}</code> if | |
97 | * available to the output stream. | |
98 | * | |
99 | * @param doc <code>{@link Document}</code> to be started, may specify a <code>{@link org.dom4j.DocumentType}</code> | |
100 | * . | |
101 | * @throws IOException a problem occurs during writing | |
102 | */ | |
103 | 63 | public void writeDocumentStart(Document doc) throws IOException |
104 | { | |
105 | 63 | writeDeclaration(); |
106 | ||
107 | 63 | if (doc.getDocType() != null) { |
108 | 0 | indent(); |
109 | 0 | writeDocType(doc.getDocType()); |
110 | } | |
111 | } | |
112 | ||
113 | /** | |
114 | * Close all remaining opened <code>{@link Element}</code> including the root element to terminate the current | |
115 | * document. Also flush the writer to ensure the whole document has been written to the | |
116 | * <code>{@link OutputStream}</code>. | |
117 | * | |
118 | * @param doc <code>{@link Document}</code> to be end, actually unused. | |
119 | * @throws IOException a problem occurs during writing. | |
120 | */ | |
121 | 63 | public void writeDocumentEnd(Document doc) throws IOException |
122 | { | |
123 | 63 | if (!this.parent.isEmpty()) { |
124 | 1 | writeClose(this.parent.firstElement()); |
125 | } | |
126 | 63 | writePrintln(); |
127 | 63 | flush(); |
128 | } | |
129 | ||
130 | /** | |
131 | * Writes the <code>{@link Element}</code>, including its <code>{@link | |
132 | * org.dom4j.Attribute}</code>s, using the <code>{@link Reader}</code> for its content. | |
133 | * <p> | |
134 | * Note that proper decoding/encoding will occurs during this operation, converting the encoding of the Reader into | |
135 | * the encoding of the Writer. | |
136 | * </p> | |
137 | * | |
138 | * @param element <code>{@link Element}</code> to output. | |
139 | * @param rd <code>{@link Reader}</code> that will be fully read and transfered into the element content. | |
140 | * @throws IOException a problem occurs during reading or writing. | |
141 | */ | |
142 | 0 | public void write(Element element, Reader rd) throws IOException |
143 | { | |
144 | 0 | writeOpen(element); |
145 | 0 | IOUtils.copy(rd, this.writer); |
146 | 0 | writeClose(element); |
147 | } | |
148 | ||
149 | /** | |
150 | * Writes the <code>{@link Element}</code>, including its <code>{@link | |
151 | * org.dom4j.Attribute}</code>s, using the <code>{@link InputStream}</code> for its content. | |
152 | * <p> | |
153 | * Note that no decoding/encoding of the InputStream will be ensured during this operation. The byte content is | |
154 | * transfered untouched. | |
155 | * </p> | |
156 | * | |
157 | * @param element <code>{@link Element}</code> to output. | |
158 | * @param is <code>{@link InputStream}</code> that will be fully read and transfered into the element content. | |
159 | * @throws IOException a problem occurs during reading or writing. | |
160 | */ | |
161 | 0 | public void write(Element element, InputStream is) throws IOException |
162 | { | |
163 | 0 | writeOpen(element); |
164 | 0 | flush(); |
165 | 0 | IOUtils.copy(is, this.out); |
166 | 0 | writeClose(element); |
167 | } | |
168 | ||
169 | /** | |
170 | * Writes the <code>{@link Element}</code>, including its <code>{@link | |
171 | * org.dom4j.Attribute}</code>s, using the <code>{@link InputStream}</code> encoded in Base64 for its content. | |
172 | * | |
173 | * @param element <code>{@link Element}</code> to output. | |
174 | * @param is <code>{@link InputStream}</code> that will be fully read and encoded in Base64 into the element | |
175 | * content. | |
176 | * @throws IOException a problem occurs during reading or writing. | |
177 | */ | |
178 | 62 | public void writeBase64(Element element, InputStream is) throws IOException |
179 | { | |
180 | 62 | writeOpen(element); |
181 | 62 | flush(); |
182 | 62 | try (Base64OutputStream base64 = new Base64OutputStream(new CloseShieldOutputStream(this.out))) { |
183 | 62 | IOUtils.copy(is, base64); |
184 | } | |
185 | 62 | writeClose(element); |
186 | } | |
187 | ||
188 | /** | |
189 | * Writes the opening tag of an {@link Element}, including its {@link org.dom4j.Attribute}s but without its content. | |
190 | * <p> | |
191 | * Compared to the DOM4J implementation, this function keeps track of opened elements. | |
192 | * </p> | |
193 | * | |
194 | * @param element <code>{@link Element}</code> to output. | |
195 | * @throws IOException a problem occurs during writing. | |
196 | * @see org.dom4j.io.XMLWriter#writeOpen(org.dom4j.Element) | |
197 | */ | |
198 | 126 | @Override |
199 | public void writeOpen(Element element) throws IOException | |
200 | { | |
201 | 126 | super.writeOpen(element); |
202 | 126 | this.parent.push(element); |
203 | } | |
204 | ||
205 | /** | |
206 | * Writes the closing tag of an {@link Element}. | |
207 | * <p> | |
208 | * Compared to the DOM4J implementation, this function ensure closing of all opened element including the one that | |
209 | * is requested to be closed. | |
210 | * </p> | |
211 | * | |
212 | * @param element <code>{@link Element}</code> to output. | |
213 | * @throws IOException a problem occurs during writing. | |
214 | * @see org.dom4j.io.XMLWriter#writeClose(org.dom4j.Element) | |
215 | */ | |
216 | 125 | @Override |
217 | public void writeClose(Element element) throws IOException | |
218 | { | |
219 | 126 | while (this.parent.peek() != element) { |
220 | 1 | super.writeClose(this.parent.pop()); |
221 | } | |
222 | 125 | super.writeClose(this.parent.pop()); |
223 | } | |
224 | } |