001package ca.uhn.fhir.util; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2016 University Health Network 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import java.util.HashMap; 024import java.util.Map; 025 026import javax.xml.namespace.NamespaceContext; 027import javax.xml.stream.XMLStreamException; 028import javax.xml.stream.XMLStreamWriter; 029 030import org.apache.commons.lang3.StringUtils; 031 032public class PrettyPrintWriterWrapper implements XMLStreamWriter { 033 034 private static final String INDENT_CHAR = " "; 035 private static final String LINEFEED_CHAR = "\n"; 036 private static final String PRE = "pre"; 037 private int depth = 0; 038 private Map<Integer, Boolean> hasChildElement = new HashMap<Integer, Boolean>(); 039 040 private int myInsidePre = 0; 041 private XMLStreamWriter myTarget; 042 private boolean myFirstIndent=true; 043 044 public PrettyPrintWriterWrapper(XMLStreamWriter target) { 045 myTarget = target; 046 } 047 048 @Override 049 public void close() throws XMLStreamException { 050 myTarget.close(); 051 } 052 053 @Override 054 public void flush() throws XMLStreamException { 055 myTarget.flush(); 056 } 057 058 @Override 059 public NamespaceContext getNamespaceContext() { 060 return myTarget.getNamespaceContext(); 061 } 062 063 @Override 064 public String getPrefix(String theUri) throws XMLStreamException { 065 return myTarget.getPrefix(theUri); 066 } 067 068 @Override 069 public Object getProperty(String theName) throws IllegalArgumentException { 070 return myTarget.getProperty(theName); 071 } 072 073 @Override 074 public void setDefaultNamespace(String theUri) throws XMLStreamException { 075 myTarget.setDefaultNamespace(theUri); 076 } 077 078 @Override 079 public void setNamespaceContext(NamespaceContext theContext) throws XMLStreamException { 080 myTarget.setNamespaceContext(theContext); 081 } 082 083 @Override 084 public void setPrefix(String thePrefix, String theUri) throws XMLStreamException { 085 myTarget.setPrefix(thePrefix, theUri); 086 } 087 088 @Override 089 public void writeAttribute(String theLocalName, String theValue) throws XMLStreamException { 090 myTarget.writeAttribute(theLocalName, theValue); 091 } 092 093 @Override 094 public void writeAttribute(String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException { 095 myTarget.writeAttribute(theNamespaceURI, theLocalName, theValue); 096 } 097 098 @Override 099 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}