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 org.sonar.api.utils.DateUtils;
023
024import javax.annotation.Nullable;
025import java.io.Writer;
026import java.util.Date;
027
028/**
029 * @since 4.2
030 */
031public class JsonWriter {
032
033  public static JsonWriter of(Writer writer) {
034    return new JsonWriter(writer);
035  }
036
037  private final com.google.gson.stream.JsonWriter stream;
038
039  private JsonWriter(Writer writer) {
040    this.stream = new com.google.gson.stream.JsonWriter(writer);
041    this.stream.setSerializeNulls(false);
042    this.stream.setLenient(false);
043  }
044
045  // for unit testing
046  JsonWriter(com.google.gson.stream.JsonWriter stream) {
047    this.stream = stream;
048  }
049
050  /**
051   * Begins encoding a new array. Each call to this method must be paired with
052   * a call to {@link #endArray}. Output is <code>[</code>.
053   *
054   * @throws org.sonar.api.utils.text.WriterException on any failure
055   */
056  public JsonWriter beginArray() {
057    try {
058      stream.beginArray();
059      return this;
060    } catch (Exception e) {
061      throw rethrow(e);
062    }
063  }
064
065  /**
066   * Ends encoding the current array. Output is <code>]</code>.
067   * @throws org.sonar.api.utils.text.WriterException on any failure
068   */
069  public JsonWriter endArray() {
070    try {
071      stream.endArray();
072      return this;
073    } catch (Exception e) {
074      throw rethrow(e);
075    }
076  }
077
078  /**
079   * Begins encoding a new object. Each call to this method must be paired
080   * with a call to {@link #endObject}. Output is <code>{</code>.
081   * @throws org.sonar.api.utils.text.WriterException on any failure
082   */
083  public JsonWriter beginObject() {
084    try {
085      stream.beginObject();
086      return this;
087    } catch (Exception e) {
088      throw rethrow(e);
089    }
090  }
091
092  /**
093   * Ends encoding the current object. Output is <code>}</code>.
094   * @throws org.sonar.api.utils.text.WriterException on any failure
095   */
096  public JsonWriter endObject() {
097    try {
098      stream.endObject();
099      return this;
100    } catch (Exception e) {
101      throw rethrow(e);
102    }
103  }
104
105  /**
106   * Encodes the property name. Output is <code>"theName":</code>.
107   * @throws org.sonar.api.utils.text.WriterException on any failure
108   */
109  public JsonWriter name(String name) {
110    try {
111      stream.name(name);
112      return this;
113    } catch (Exception e) {
114      throw rethrow(e);
115    }
116  }
117
118  /**
119   * Encodes {@code value}. Output is <code>true</code> or <code>false</code>.
120   * @throws org.sonar.api.utils.text.WriterException on any failure
121   */
122  public JsonWriter value(boolean value) {
123    try {
124      stream.value(value);
125      return this;
126    } catch (Exception e) {
127      throw rethrow(e);
128    }
129  }
130
131  /**
132   * @throws org.sonar.api.utils.text.WriterException on any failure
133   */
134  public JsonWriter value(double value) {
135    try {
136      stream.value(value);
137      return this;
138    } catch (Exception e) {
139      throw rethrow(e);
140    }
141  }
142
143  /**
144   * @throws org.sonar.api.utils.text.WriterException on any failure
145   */
146  public JsonWriter value(@Nullable String value) {
147    try {
148      stream.value(value);
149      return this;
150    } catch (Exception e) {
151      throw rethrow(e);
152    }
153  }
154
155  /**
156   * @throws org.sonar.api.utils.text.WriterException on any failure
157   */
158  public JsonWriter valueDate(@Nullable Date value) {
159    try {
160      stream.value(value==null ? null : DateUtils.formatDate(value));
161      return this;
162    } catch (Exception e) {
163      throw rethrow(e);
164    }
165  }
166
167  public JsonWriter valueDateTime(@Nullable Date value) {
168    try {
169      stream.value(value==null ? null : DateUtils.formatDateTime(value));
170      return this;
171    } catch (Exception e) {
172      throw rethrow(e);
173    }
174  }
175
176  /**
177   * @throws org.sonar.api.utils.text.WriterException on any failure
178   */
179  public JsonWriter value(long value) {
180    try {
181      stream.value(value);
182      return this;
183    } catch (Exception e) {
184      throw rethrow(e);
185    }
186  }
187
188  /**
189   * @throws org.sonar.api.utils.text.WriterException on any failure
190   */
191  public JsonWriter value(@Nullable Number value) {
192    try {
193      stream.value(value);
194      return this;
195    } catch (Exception e) {
196      throw rethrow(e);
197    }
198  }
199
200  /**
201   * Encodes the property name and value. Output is for example <code>"theName":123</code>.
202   * @throws org.sonar.api.utils.text.WriterException on any failure
203   */
204  public JsonWriter prop(String name, @Nullable Number value) {
205    return name(name).value(value);
206  }
207
208  /**
209   * Encodes the property name and date value (ISO format).
210   * Output is for example <code>"theDate":"2013-01-24"</code>.
211   * @throws org.sonar.api.utils.text.WriterException on any failure
212   */
213  public JsonWriter propDate(String name, @Nullable Date value) {
214    return name(name).valueDate(value);
215  }
216
217  /**
218   * Encodes the property name and datetime value (ISO format).
219   * Output is for example <code>"theDate":"2013-01-24T13:12:45+01"</code>.
220   * @throws org.sonar.api.utils.text.WriterException on any failure
221   */
222  public JsonWriter propDateTime(String name, @Nullable Date value) {
223    return name(name).valueDateTime(value);
224  }
225
226  /**
227   * @throws org.sonar.api.utils.text.WriterException on any failure
228   */
229  public JsonWriter prop(String name, @Nullable String value) {
230    return name(name).value(value);
231  }
232
233  /**
234   * @throws org.sonar.api.utils.text.WriterException on any failure
235   */
236  public JsonWriter prop(String name, boolean value) {
237    return name(name).value(value);
238  }
239
240  /**
241   * @throws org.sonar.api.utils.text.WriterException on any failure
242   */
243  public JsonWriter prop(String name, long value) {
244    return name(name).value(value);
245  }
246
247  /**
248   * @throws org.sonar.api.utils.text.WriterException on any failure
249   */
250  public JsonWriter prop(String name, double value) {
251    return name(name).value(value);
252  }
253
254  /**
255   * @throws org.sonar.api.utils.text.WriterException on any failure
256   */
257  public void close() {
258    try {
259      stream.close();
260    } catch (Exception e) {
261      throw rethrow(e);
262    }
263  }
264
265  private IllegalStateException rethrow(Exception e) {
266    // stacktrace is not helpful
267    throw new WriterException("Fail to write JSON: " + e.getMessage());
268  }
269}