001 /*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * SonarQube is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
019 */
020 package org.sonar.api.utils.text;
021
022 import javax.annotation.Nullable;
023 import javax.xml.stream.XMLOutputFactory;
024 import javax.xml.stream.XMLStreamException;
025 import javax.xml.stream.XMLStreamWriter;
026 import java.io.Writer;
027
028 /**
029 * TODO document that output is UTF-8
030 */
031 public class XmlWriter {
032
033 private final XMLStreamWriter stream;
034
035 private XmlWriter(Writer writer) {
036 try {
037 XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance();
038 stream = xmlFactory.createXMLStreamWriter(writer);
039 } catch (Exception e) {
040 throw new WriterException("Fail to initialize XML Stream writer", e);
041 }
042 }
043
044 public static XmlWriter of(Writer writer) {
045 return new XmlWriter(writer);
046 }
047
048 public XmlWriter declaration() {
049 try {
050 stream.writeStartDocument();
051 return this;
052 } catch (XMLStreamException e) {
053 throw rethrow(e);
054 }
055 }
056
057 public XmlWriter begin(String nodeName) {
058 try {
059 stream.writeStartElement(nodeName);
060 return this;
061 } catch (XMLStreamException e) {
062 throw rethrow(e);
063 }
064 }
065
066
067 public XmlWriter end() {
068 try {
069 stream.writeEndElement();
070 return this;
071 } catch (XMLStreamException e) {
072 throw rethrow(e);
073 }
074 }
075
076 /**
077 * Same as {@link #end()}. The parameter is unused. It's declared only to improve
078 * readability :
079 * <pre>
080 * xml.write("rules");
081 * xml.write("rule");
082 * // many other writes
083 * xml.end("rule");
084 * xml.end("rules");
085 * </pre>
086 */
087 public XmlWriter end(String unused) {
088 return end();
089 }
090
091 public XmlWriter prop(String nodeName, @Nullable String value) {
092 if (value != null) {
093 begin(nodeName).value(value).end();
094 }
095 return this;
096 }
097
098 public XmlWriter prop(String nodeName, @Nullable Number value) {
099 if (value != null) {
100 begin(nodeName).value(value).end();
101 }
102 return this;
103 }
104
105 public XmlWriter prop(String nodeName, boolean value) {
106 return begin(nodeName).value(value).end();
107 }
108
109 public XmlWriter prop(String nodeName, long value) {
110 return begin(nodeName).value(value).end();
111 }
112
113 public XmlWriter prop(String nodeName, double value) {
114 return begin(nodeName).value(value).end();
115 }
116
117 private XmlWriter value(boolean value) {
118 try {
119 stream.writeCharacters(String.valueOf(value));
120 return this;
121 } catch (XMLStreamException e) {
122 throw rethrow(e);
123 }
124 }
125
126 private XmlWriter value(double value) {
127 try {
128 if (Double.isNaN(value) || Double.isInfinite(value)) {
129 throw new WriterException("Fail to write XML. Double value is not valid: " + value);
130 }
131 stream.writeCharacters(String.valueOf(value));
132 return this;
133 } catch (XMLStreamException e) {
134 throw rethrow(e);
135 }
136 }
137
138 private XmlWriter value(@Nullable String value) {
139 try {
140 if (value != null) {
141 stream.writeCharacters(value);
142 }
143 return this;
144 } catch (XMLStreamException e) {
145 throw rethrow(e);
146 }
147 }
148
149 private XmlWriter value(long value) {
150 try {
151 stream.writeCharacters(String.valueOf(value));
152 return this;
153 } catch (XMLStreamException e) {
154 throw rethrow(e);
155 }
156 }
157
158 private XmlWriter value(@Nullable Number value) {
159 try {
160 if (value != null) {
161 stream.writeCharacters(String.valueOf(value));
162 }
163 return this;
164 } catch (XMLStreamException e) {
165 throw rethrow(e);
166 }
167 }
168
169 public void close() {
170 try {
171 stream.writeEndDocument();
172 stream.flush();
173 stream.close();
174 } catch (XMLStreamException e) {
175 throw rethrow(e);
176 }
177 }
178
179 private IllegalStateException rethrow(XMLStreamException e) {
180 // stacktrace is not helpful
181 throw new IllegalStateException("Fail to write XML: " + e.getMessage());
182 }
183 }