View Javadoc
1   package ca.uhn.fhir.util;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 - 2018 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import java.util.HashMap;
24  import java.util.Map;
25  
26  import javax.xml.namespace.NamespaceContext;
27  import javax.xml.stream.XMLStreamException;
28  import javax.xml.stream.XMLStreamWriter;
29  
30  import org.apache.commons.lang3.StringUtils;
31  
32  public class PrettyPrintWriterWrapper implements XMLStreamWriter {
33  
34  	private static final String INDENT_CHAR = " ";
35  	private static final String LINEFEED_CHAR = "\n";
36  	private static final String PRE = "pre";
37  	private int depth = 0;
38  	private Map<Integer, Boolean> hasChildElement = new HashMap<Integer, Boolean>();
39  
40  	private int myInsidePre = 0;
41  	private XMLStreamWriter myTarget;
42  	private boolean myFirstIndent=true;
43  
44  	public PrettyPrintWriterWrapper(XMLStreamWriter target) {
45  		myTarget = target;
46  	}
47  
48  	@Override
49  	public void close() throws XMLStreamException {
50  		myTarget.close();
51  	}
52  
53  	@Override
54  	public void flush() throws XMLStreamException {
55  		myTarget.flush();
56  	}
57  
58  	@CoverageIgnore
59  	@Override
60  	public NamespaceContext getNamespaceContext() {
61  		return myTarget.getNamespaceContext();
62  	}
63  
64  	@CoverageIgnore
65  	@Override
66  	public String getPrefix(String theUri) throws XMLStreamException {
67  		return myTarget.getPrefix(theUri);
68  	}
69  
70  	@CoverageIgnore
71  	@Override
72  	public Object getProperty(String theName) throws IllegalArgumentException {
73  		return myTarget.getProperty(theName);
74  	}
75  
76  	@CoverageIgnore
77  	@Override
78  	public void setDefaultNamespace(String theUri) throws XMLStreamException {
79  		myTarget.setDefaultNamespace(theUri);
80  	}
81  
82  	@CoverageIgnore
83  	@Override
84  	public void setNamespaceContext(NamespaceContext theContext) throws XMLStreamException {
85  		myTarget.setNamespaceContext(theContext);
86  	}
87  
88  	@CoverageIgnore
89  	@Override
90  	public void setPrefix(String thePrefix, String theUri) throws XMLStreamException {
91  		myTarget.setPrefix(thePrefix, theUri);
92  	}
93  
94  	@Override
95  	public void writeAttribute(String theLocalName, String theValue) throws XMLStreamException {
96  		myTarget.writeAttribute(theLocalName, theValue);
97  	}
98  
99  	@CoverageIgnore
100 	@Override
101 	public void writeAttribute(String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
102 		myTarget.writeAttribute(theNamespaceURI, theLocalName, theValue);
103 	}
104 
105 	@CoverageIgnore
106 	@Override
107 	public void writeAttribute(String thePrefix, String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
108 		myTarget.writeAttribute(thePrefix, theNamespaceURI, theLocalName, theValue);
109 	}
110 
111 	@CoverageIgnore
112 	@Override
113 	public void writeCData(String theData) throws XMLStreamException {
114 		myTarget.writeCData(theData);
115 	}
116 
117 	@Override
118 	public void writeCharacters(char[] theText, int theStart, int theLen) throws XMLStreamException {
119 		NonPrettyPrintWriterWrapper.writeCharacters(theText, theStart, theLen, myTarget, myInsidePre);
120 	}
121 
122 	@Override
123 	public void writeCharacters(String theText) throws XMLStreamException {
124 		if (myInsidePre > 0) {
125 			myTarget.writeCharacters(theText);
126 		} else {
127 			writeCharacters(theText.toCharArray(), 0, theText.length());
128 		}
129 	}
130 
131 	@Override
132 	public void writeComment(String theData) throws XMLStreamException {
133 		indent();
134 		myTarget.writeComment(theData);
135 	}
136 
137 	@Override
138 	public void writeDefaultNamespace(String theNamespaceURI) throws XMLStreamException {
139 		myTarget.writeDefaultNamespace(theNamespaceURI);
140 	}
141 
142 	@CoverageIgnore
143 	@Override
144 	public void writeDTD(String theDtd) throws XMLStreamException {
145 		myTarget.writeDTD(theDtd);
146 	}
147 
148 	@CoverageIgnore
149 	@Override
150 	public void writeEmptyElement(String theLocalName) throws XMLStreamException {
151 		indent();
152 		myTarget.writeEmptyElement(theLocalName);
153 	}
154 
155 	@CoverageIgnore
156 	@Override
157 	public void writeEmptyElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
158 		indent();
159 		myTarget.writeEmptyElement(theNamespaceURI, theLocalName);
160 	}
161 
162 	@CoverageIgnore
163 	@Override
164 	public void writeEmptyElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
165 		indent();
166 		myTarget.writeEmptyElement(thePrefix, theLocalName, theNamespaceURI);
167 	}
168 
169 	@CoverageIgnore
170 	@Override
171 	public void writeEndDocument() throws XMLStreamException {
172 		decrementAndIndent();
173 		myTarget.writeEndDocument();
174 	}
175 
176 	@Override
177 	public void writeEndElement() throws XMLStreamException {
178 		if (myInsidePre > 0) {
179 			myInsidePre--;
180 		}
181 		decrementAndIndent();
182 
183 		myTarget.writeEndElement();
184 
185 	}
186 
187 	@CoverageIgnore
188 	@Override
189 	public void writeEntityRef(String theName) throws XMLStreamException {
190 		myTarget.writeEntityRef(theName);
191 	}
192 
193 	@Override
194 	public void writeNamespace(String thePrefix, String theNamespaceURI) throws XMLStreamException {
195 		myTarget.writeNamespace(thePrefix, theNamespaceURI);
196 	}
197 
198 	@CoverageIgnore
199 	@Override
200 	public void writeProcessingInstruction(String theTarget) throws XMLStreamException {
201 		myTarget.writeProcessingInstruction(theTarget);
202 	}
203 
204 	@CoverageIgnore
205 	@Override
206 	public void writeProcessingInstruction(String theTarget, String theData) throws XMLStreamException {
207 		myTarget.writeProcessingInstruction(theTarget, theData);
208 	}
209 
210 	@Override
211 	public void writeStartDocument() throws XMLStreamException {
212 		myFirstIndent=true;
213 		myTarget.writeStartDocument();
214 	}
215 
216 	@Override
217 	public void writeStartDocument(String theVersion) throws XMLStreamException {
218 		myFirstIndent=true;
219 		myTarget.writeStartDocument(theVersion);
220 	}
221 
222 	@Override
223 	public void writeStartDocument(String theEncoding, String theVersion) throws XMLStreamException {
224 		myFirstIndent=true;
225 		myTarget.writeStartDocument(theEncoding, theVersion);
226 	}
227 
228 	@Override
229 	public void writeStartElement(String theLocalName) throws XMLStreamException {
230 		indentAndAdd();
231 		myTarget.writeStartElement(theLocalName);
232 		if (PRE.equals(theLocalName) || myInsidePre > 0) {
233 			myInsidePre++;
234 		}
235 	}
236 
237 	@Override
238 	public void writeStartElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
239 		indentAndAdd();
240 		myTarget.writeStartElement(theNamespaceURI, theLocalName);
241 		if (PRE.equals(theLocalName) || myInsidePre > 0) {
242 			myInsidePre++;
243 		}
244 	}
245 
246 	@Override
247 	public void writeStartElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
248 		indentAndAdd();
249 		myTarget.writeStartElement(thePrefix, theLocalName, theNamespaceURI);
250 		if (PRE.equals(theLocalName) || myInsidePre > 0) {
251 			myInsidePre++;
252 		}
253 	}
254 
255 	private void decrementAndIndent() throws XMLStreamException {
256 		if (myInsidePre > 0) {
257 			return;
258 		}
259 		depth--;
260 
261 		if (hasChildElement.get(depth) == true) {
262 			// indent for current depth
263 			myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
264 		}
265 	}
266 
267 	private void indent() throws XMLStreamException {
268 		if (myFirstIndent) {
269 			myFirstIndent = false;
270 			return;
271 		}
272 		myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
273 	}
274 
275 	private void indentAndAdd() throws XMLStreamException {
276 		if (myInsidePre > 0) {
277 			return;
278 		}
279 		indent();
280 
281 		// update state of parent node
282 		if (depth > 0) {
283 			hasChildElement.put(depth - 1, true);
284 		}
285 
286 		// reset state of current node
287 		hasChildElement.put(depth, false);
288 
289 		depth++;
290 	}
291 
292 	private String repeat(int d, String s) {
293 		return StringUtils.repeat(s, d * 3);
294 	}
295 
296 }