001/* 002 * SonarQube 003 * Copyright (C) 2009-2017 SonarSource SA 004 * mailto:info AT sonarsource DOT com 005 * 006 * This program 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 * This program 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 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 static IllegalStateException rethrow(XMLStreamException e) { 180 // stacktrace is not helpful 181 throw new IllegalStateException("Fail to write XML: " + e.getMessage()); 182 } 183}