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      public XmlWriter prop(String nodeName, @Nullable String value) {
077        if (value != null) {
078          begin(nodeName).value(value).end();
079        }
080        return this;
081      }
082    
083      public XmlWriter prop(String nodeName, @Nullable Number value) {
084        if (value != null) {
085          begin(nodeName).value(value).end();
086        }
087        return this;
088      }
089    
090      public XmlWriter prop(String nodeName, boolean value) {
091        return begin(nodeName).value(value).end();
092      }
093    
094      public XmlWriter prop(String nodeName, long value) {
095        return begin(nodeName).value(value).end();
096      }
097    
098      public XmlWriter prop(String nodeName, double value) {
099        return begin(nodeName).value(value).end();
100      }
101    
102      private XmlWriter value(boolean value) {
103        try {
104          stream.writeCharacters(String.valueOf(value));
105          return this;
106        } catch (XMLStreamException e) {
107          throw rethrow(e);
108        }
109      }
110    
111      private XmlWriter value(double value) {
112        try {
113          if (Double.isNaN(value) || Double.isInfinite(value)) {
114            throw new WriterException("Fail to write XML. Double value is not valid: " + value);
115          }
116          stream.writeCharacters(String.valueOf(value));
117          return this;
118        } catch (XMLStreamException e) {
119          throw rethrow(e);
120        }
121      }
122    
123      private XmlWriter value(@Nullable String value) {
124        try {
125          if (value != null) {
126            stream.writeCharacters(value);
127          }
128          return this;
129        } catch (XMLStreamException e) {
130          throw rethrow(e);
131        }
132      }
133    
134      private XmlWriter value(long value) {
135        try {
136          stream.writeCharacters(String.valueOf(value));
137          return this;
138        } catch (XMLStreamException e) {
139          throw rethrow(e);
140        }
141      }
142    
143      private XmlWriter value(@Nullable Number value) {
144        try {
145          if (value != null) {
146            stream.writeCharacters(String.valueOf(value));
147          }
148          return this;
149        } catch (XMLStreamException e) {
150          throw rethrow(e);
151        }
152      }
153    
154      public void close() {
155        try {
156          stream.writeEndDocument();
157          stream.flush();
158          stream.close();
159        } catch (XMLStreamException e) {
160          throw rethrow(e);
161        }
162      }
163    
164      private IllegalStateException rethrow(XMLStreamException e) {
165        // stacktrace is not helpful
166        throw new IllegalStateException("Fail to write XML: " + e.getMessage());
167      }
168    }