View Javadoc
1   package ca.uhn.fhir.util;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 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  	@Override
59  	public NamespaceContext getNamespaceContext() {
60  		return myTarget.getNamespaceContext();
61  	}
62  
63  	@Override
64  	public String getPrefix(String theUri) throws XMLStreamException {
65  		return myTarget.getPrefix(theUri);
66  	}
67  
68  	@Override
69  	public Object getProperty(String theName) throws IllegalArgumentException {
70  		return myTarget.getProperty(theName);
71  	}
72  
73  	@Override
74  	public void setDefaultNamespace(String theUri) throws XMLStreamException {
75  		myTarget.setDefaultNamespace(theUri);
76  	}
77  
78  	@Override
79  	public void setNamespaceContext(NamespaceContext theContext) throws XMLStreamException {
80  		myTarget.setNamespaceContext(theContext);
81  	}
82  
83  	@Override
84  	public void setPrefix(String thePrefix, String theUri) throws XMLStreamException {
85  		myTarget.setPrefix(thePrefix, theUri);
86  	}
87  
88  	@Override
89  	public void writeAttribute(String theLocalName, String theValue) throws XMLStreamException {
90  		myTarget.writeAttribute(theLocalName, theValue);
91  	}
92  
93  	@Override
94  	public void writeAttribute(String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
95  		myTarget.writeAttribute(theNamespaceURI, theLocalName, theValue);
96  	}
97  
98  	@Override
99  	public void writeAttribute(String thePrefix, String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
100 		myTarget.writeAttribute(thePrefix, theNamespaceURI, theLocalName, theValue);
101 	}
102 
103 	@Override
104 	public void writeCData(String theData) throws XMLStreamException {
105 		myTarget.writeCData(theData);
106 	}
107 
108 	@Override
109 	public void writeCharacters(char[] theText, int theStart, int theLen) throws XMLStreamException {
110 		NonPrettyPrintWriterWrapper.writeCharacters(theText, theStart, theLen, myTarget, myInsidePre);
111 	}
112 
113 	@Override
114 	public void writeCharacters(String theText) throws XMLStreamException {
115 		if (myInsidePre > 0) {
116 			myTarget.writeCharacters(theText);
117 		} else {
118 			writeCharacters(theText.toCharArray(), 0, theText.length());
119 		}
120 	}
121 
122 	@Override
123 	public void writeComment(String theData) throws XMLStreamException {
124 		myTarget.writeComment(theData);
125 	}
126 
127 	@Override
128 	public void writeDefaultNamespace(String theNamespaceURI) throws XMLStreamException {
129 		myTarget.writeDefaultNamespace(theNamespaceURI);
130 	}
131 
132 	@Override
133 	public void writeDTD(String theDtd) throws XMLStreamException {
134 		myTarget.writeDTD(theDtd);
135 	}
136 
137 	@Override
138 	public void writeEmptyElement(String theLocalName) throws XMLStreamException {
139 		indent();
140 		myTarget.writeEmptyElement(theLocalName);
141 	}
142 
143 	@Override
144 	public void writeEmptyElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
145 		indent();
146 		myTarget.writeEmptyElement(theNamespaceURI, theLocalName);
147 	}
148 
149 	@Override
150 	public void writeEmptyElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
151 		indent();
152 		myTarget.writeEmptyElement(thePrefix, theLocalName, theNamespaceURI);
153 	}
154 
155 	@Override
156 	public void writeEndDocument() throws XMLStreamException {
157 		decrementAndIndent();
158 		myTarget.writeEndDocument();
159 	}
160 
161 	@Override
162 	public void writeEndElement() throws XMLStreamException {
163 		if (myInsidePre > 0) {
164 			myInsidePre--;
165 		}
166 		decrementAndIndent();
167 
168 		myTarget.writeEndElement();
169 
170 	}
171 
172 	@Override
173 	public void writeEntityRef(String theName) throws XMLStreamException {
174 		myTarget.writeEntityRef(theName);
175 	}
176 
177 	@Override
178 	public void writeNamespace(String thePrefix, String theNamespaceURI) throws XMLStreamException {
179 		myTarget.writeNamespace(thePrefix, theNamespaceURI);
180 	}
181 
182 	@Override
183 	public void writeProcessingInstruction(String theTarget) throws XMLStreamException {
184 		myTarget.writeProcessingInstruction(theTarget);
185 	}
186 
187 	@Override
188 	public void writeProcessingInstruction(String theTarget, String theData) throws XMLStreamException {
189 		myTarget.writeProcessingInstruction(theTarget, theData);
190 	}
191 
192 	@Override
193 	public void writeStartDocument() throws XMLStreamException {
194 		myFirstIndent=true;
195 		myTarget.writeStartDocument();
196 	}
197 
198 	@Override
199 	public void writeStartDocument(String theVersion) throws XMLStreamException {
200 		myFirstIndent=true;
201 		myTarget.writeStartDocument(theVersion);
202 	}
203 
204 	@Override
205 	public void writeStartDocument(String theEncoding, String theVersion) throws XMLStreamException {
206 		myFirstIndent=true;
207 		myTarget.writeStartDocument(theEncoding, theVersion);
208 	}
209 
210 	@Override
211 	public void writeStartElement(String theLocalName) throws XMLStreamException {
212 		indentAndAdd();
213 		myTarget.writeStartElement(theLocalName);
214 		if (PRE.equals(theLocalName) || myInsidePre > 0) {
215 			myInsidePre++;
216 		}
217 	}
218 
219 	@Override
220 	public void writeStartElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
221 		indentAndAdd();
222 		myTarget.writeStartElement(theNamespaceURI, theLocalName);
223 		if (PRE.equals(theLocalName) || myInsidePre > 0) {
224 			myInsidePre++;
225 		}
226 	}
227 
228 	@Override
229 	public void writeStartElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
230 		indentAndAdd();
231 		myTarget.writeStartElement(thePrefix, theLocalName, theNamespaceURI);
232 		if (PRE.equals(theLocalName) || myInsidePre > 0) {
233 			myInsidePre++;
234 		}
235 	}
236 
237 	private void decrementAndIndent() throws XMLStreamException {
238 		if (myInsidePre > 0) {
239 			return;
240 		}
241 		depth--;
242 
243 		if (hasChildElement.get(depth) == true) {
244 			// indent for current depth
245 			myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
246 		}
247 	}
248 
249 	private void indent() throws XMLStreamException {
250 		if (myFirstIndent) {
251 			myFirstIndent = false;
252 			return;
253 		}
254 		myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
255 	}
256 
257 	private void indentAndAdd() throws XMLStreamException {
258 		if (myInsidePre > 0) {
259 			return;
260 		}
261 		indent();
262 
263 		// update state of parent node
264 		if (depth > 0) {
265 			hasChildElement.put(depth - 1, true);
266 		}
267 
268 		// reset state of current node
269 		hasChildElement.put(depth, false);
270 
271 		depth++;
272 	}
273 
274 	private String repeat(int d, String s) {
275 		return StringUtils.repeat(s, d * 3);
276 	}
277 
278 }