View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.metaschema.core.model.xml;
28  
29  import org.codehaus.stax2.XMLStreamWriter2;
30  import org.codehaus.stax2.util.StreamWriter2Delegate;
31  
32  import java.util.HashMap;
33  import java.util.Map;
34  import java.util.Objects;
35  
36  import javax.xml.stream.XMLStreamException;
37  
38  public class IndentingXmlStreamWriter2
39      extends StreamWriter2Delegate {
40    private String indentText = DEFAULT_INDENT_TEXT;
41    private String lineEndText = DEFAULT_LINE_END_TEXT;
42    private int depth; // = 0;
43    private final Map<Integer, Boolean> depthWithChildMap = new HashMap<>(); // NOPMD - synchronization not needed
44    private static final String DEFAULT_INDENT_TEXT = "  ";
45    private static final String DEFAULT_LINE_END_TEXT = "\n";
46  
47    public IndentingXmlStreamWriter2(XMLStreamWriter2 parent) {
48      super(parent);
49    }
50  
51    protected String getIndentText() {
52      return indentText;
53    }
54  
55    protected void setIndentText(String indentText) {
56      Objects.requireNonNull(indentText, "indentText");
57      this.indentText = indentText;
58    }
59  
60    protected String getLineEndText() {
61      return lineEndText;
62    }
63  
64    protected void setLineEndText(String lineEndText) {
65      Objects.requireNonNull(lineEndText, "lineEndText");
66      this.lineEndText = lineEndText;
67    }
68  
69    protected void handleStartElement() throws XMLStreamException {
70      // update state of parent node
71      if (depth > 0) {
72        depthWithChildMap.put(depth - 1, true);
73      }
74      // reset state of current node
75      depthWithChildMap.put(depth, false);
76      // indent for current depth
77      getParent().writeCharacters(getLineEndText());
78      getParent().writeCharacters(getIndentText().repeat(depth));
79      depth++;
80    }
81  
82    @Override
83    public void writeStartElement(String localName) throws XMLStreamException {
84      handleStartElement();
85      super.writeStartElement(localName);
86    }
87  
88    @Override
89    public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
90      handleStartElement();
91      super.writeStartElement(namespaceURI, localName);
92    }
93  
94    @Override
95    public void writeStartElement(String prefix,
96        String localName,
97        String namespaceURI) throws XMLStreamException {
98      handleStartElement();
99      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 }