001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.core.model.xml; 028 029import org.codehaus.stax2.XMLStreamWriter2; 030import org.codehaus.stax2.util.StreamWriter2Delegate; 031 032import java.util.HashMap; 033import java.util.Map; 034import java.util.Objects; 035 036import javax.xml.stream.XMLStreamException; 037 038public class IndentingXmlStreamWriter2 039 extends StreamWriter2Delegate { 040 private String indentText = DEFAULT_INDENT_TEXT; 041 private String lineEndText = DEFAULT_LINE_END_TEXT; 042 private int depth; // = 0; 043 private final Map<Integer, Boolean> depthWithChildMap = new HashMap<>(); // NOPMD - synchronization not needed 044 private static final String DEFAULT_INDENT_TEXT = " "; 045 private static final String DEFAULT_LINE_END_TEXT = "\n"; 046 047 public IndentingXmlStreamWriter2(XMLStreamWriter2 parent) { 048 super(parent); 049 } 050 051 protected String getIndentText() { 052 return indentText; 053 } 054 055 protected void setIndentText(String indentText) { 056 Objects.requireNonNull(indentText, "indentText"); 057 this.indentText = indentText; 058 } 059 060 protected String getLineEndText() { 061 return lineEndText; 062 } 063 064 protected void setLineEndText(String lineEndText) { 065 Objects.requireNonNull(lineEndText, "lineEndText"); 066 this.lineEndText = lineEndText; 067 } 068 069 protected void handleStartElement() throws XMLStreamException { 070 // update state of parent node 071 if (depth > 0) { 072 depthWithChildMap.put(depth - 1, true); 073 } 074 // reset state of current node 075 depthWithChildMap.put(depth, false); 076 // indent for current depth 077 getParent().writeCharacters(getLineEndText()); 078 getParent().writeCharacters(getIndentText().repeat(depth)); 079 depth++; 080 } 081 082 @Override 083 public void writeStartElement(String localName) throws XMLStreamException { 084 handleStartElement(); 085 super.writeStartElement(localName); 086 } 087 088 @Override 089 public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException { 090 handleStartElement(); 091 super.writeStartElement(namespaceURI, localName); 092 } 093 094 @Override 095 public void writeStartElement(String prefix, 096 String localName, 097 String namespaceURI) throws XMLStreamException { 098 handleStartElement(); 099 super.writeStartElement(prefix, localName, namespaceURI); 100 } 101 102 protected void handleEndElement() throws XMLStreamException { 103 depth--; 104 if (depthWithChildMap.get(depth)) { 105 getParent().writeCharacters(getLineEndText()); 106 getParent().writeCharacters(getIndentText().repeat(depth)); 107 } 108 } 109 110 @Override 111 public void writeEndElement() throws XMLStreamException { 112 handleEndElement(); 113 super.writeEndElement(); 114 } 115 116 @Override 117 public void writeFullEndElement() throws XMLStreamException { 118 handleEndElement(); 119 super.writeFullEndElement(); 120 } 121 122 protected void handleEmptyElement() throws XMLStreamException { 123 // update state of parent node 124 if (depth > 0) { 125 depthWithChildMap.put(depth - 1, true); 126 } 127 // indent for current depth 128 getParent().writeCharacters(getLineEndText()); 129 getParent().writeCharacters(getIndentText().repeat(depth)); 130 } 131 132 @Override 133 public void writeEmptyElement(String localName) throws XMLStreamException { 134 handleEmptyElement(); 135 super.writeEmptyElement(localName); 136 } 137 138 @Override 139 public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException { 140 handleEmptyElement(); 141 super.writeEmptyElement(namespaceURI, localName); 142 } 143 144 @Override 145 public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException { 146 handleEmptyElement(); 147 super.writeEmptyElement(prefix, localName, namespaceURI); 148 } 149}