001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2013 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 */
020package org.sonar.api.utils.text;
021
022import javax.annotation.Nullable;
023import javax.xml.stream.XMLOutputFactory;
024import javax.xml.stream.XMLStreamException;
025import javax.xml.stream.XMLStreamWriter;
026import java.io.Writer;
027
028/**
029 * TODO document that output is UTF-8
030 */
031public class XmlWriter {
032
033  public static XmlWriter of(Writer writer) {
034    return new XmlWriter(writer);
035  }
036
037  private final XMLStreamWriter stream;
038
039  private XmlWriter(Writer writer) {
040    try {
041      XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance();
042      stream = xmlFactory.createXMLStreamWriter(writer);
043    } catch (Exception e) {
044      throw new WriterException("Fail to initialize XML Stream writer", e);
045    }
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}