]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5292 Provide a new "/api/sources/raw" WS
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 29 Oct 2014 11:41:51 +0000 (12:41 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 29 Oct 2014 11:59:31 +0000 (12:59 +0100)
16 files changed:
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/plugins/MimeTypes.java
server/sonar-server/src/main/java/org/sonar/server/source/SourceService.java
server/sonar-server/src/main/java/org/sonar/server/source/ws/RawAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/source/ws/SourcesWs.java
server/sonar-server/src/main/java/org/sonar/server/ws/ServletResponse.java
server/sonar-server/src/main/resources/org/sonar/server/source/ws/example-raw.txt [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/source/ws/RawActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/source/ws/ScmActionTest.java
server/sonar-server/src/test/java/org/sonar/server/source/ws/ShowActionTest.java
server/sonar-server/src/test/java/org/sonar/server/source/ws/SourcesWsTest.java
server/sonar-server/src/test/java/org/sonar/server/ws/WsTester.java
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Response.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/text/TxtWriter.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/utils/text/TxtWriterTest.java [new file with mode: 0644]

index f19c67df03366d95606b9eef373fa93082b011e7..95b36d3dad3e60aeecf81a33158887f4e87fbfa6 100644 (file)
@@ -163,10 +163,8 @@ import org.sonar.server.source.CodeColorizers;
 import org.sonar.server.source.DeprecatedSourceDecorator;
 import org.sonar.server.source.HtmlSourceDecorator;
 import org.sonar.server.source.SourceService;
-import org.sonar.server.source.ws.ScmAction;
-import org.sonar.server.source.ws.ScmWriter;
+import org.sonar.server.source.ws.*;
 import org.sonar.server.source.ws.ShowAction;
-import org.sonar.server.source.ws.SourcesWs;
 import org.sonar.server.startup.*;
 import org.sonar.server.test.CoverageService;
 import org.sonar.server.test.ws.*;
@@ -543,6 +541,7 @@ class ServerComponents {
     pico.addSingleton(SourcesWs.class);
     pico.addSingleton(ShowAction.class);
     pico.addSingleton(ScmWriter.class);
+    pico.addSingleton(RawAction.class);
     pico.addSingleton(ScmAction.class);
 
     // Duplications
index 8e51c352dcb09145de092f3633adab0ca38f5fef..b5319ca37663af469425c0cd4aac21fe6f7999e6 100644 (file)
@@ -33,6 +33,7 @@ public final class MimeTypes {
 
   public static final String JSON = "application/json";
   public static final String XML = "application/xml";
+  public static final String TXT = "text/plain";
   public static final String DEFAULT = "application/octet-stream";
 
   private static final Map<String, String> MAP = new ImmutableMap.Builder<String, String>()
@@ -56,7 +57,7 @@ public final class MimeTypes {
     .put("png", "image/png")
     .put("svg", "image/svg+xml")
     .put("ico", "image/x-icon")
-    .put("txt", "text/plain")
+    .put("txt", TXT)
     .put("csv", "text/csv")
     .put("properties", "text/plain")
     .put("rtf", "text/rtf")
index 8e2733046fb8350e431ba6d82b9337bbe51ae231..61755cac9d78697920928fcfdc5cdf2889c13bc9 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.sonar.server.source;
 
+import com.google.common.base.Splitter;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.web.UserRole;
@@ -27,27 +28,33 @@ import org.sonar.core.measure.db.MeasureDto;
 import org.sonar.core.measure.db.MeasureKey;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
+import org.sonar.core.source.db.SnapshotSourceDao;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.user.UserSession;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
+import java.util.Collections;
 import java.util.List;
 
+import static com.google.common.collect.Lists.newArrayList;
+
 public class SourceService implements ServerComponent {
 
   private final DbClient dbClient;
   private final HtmlSourceDecorator sourceDecorator;
+  private final SnapshotSourceDao snapshotSourceDao;
 
   /**
    * Old service to colorize code
    */
   private final DeprecatedSourceDecorator deprecatedSourceDecorator;
 
-  public SourceService(DbClient dbClient, HtmlSourceDecorator sourceDecorator, DeprecatedSourceDecorator deprecatedSourceDecorator) {
+  public SourceService(DbClient dbClient, HtmlSourceDecorator sourceDecorator, SnapshotSourceDao snapshotSourceDao, DeprecatedSourceDecorator deprecatedSourceDecorator) {
     this.dbClient = dbClient;
     this.sourceDecorator = sourceDecorator;
+    this.snapshotSourceDao = snapshotSourceDao;
     this.deprecatedSourceDecorator = deprecatedSourceDecorator;
   }
 
@@ -67,6 +74,21 @@ public class SourceService implements ServerComponent {
     return deprecatedSourceDecorator.getSourceAsHtml(fileKey, from, to);
   }
 
+  public List<String> getLinesAsTxt(String fileKey) {
+    checkPermission(fileKey);
+
+    DbSession session = dbClient.openSession(false);
+    try {
+      String source = snapshotSourceDao.selectSnapshotSourceByComponentKey(fileKey, session);
+      if (source != null) {
+        return newArrayList(Splitter.onPattern("\r?\n|\r").split(source));
+      }
+      return Collections.emptyList();
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+
   @CheckForNull
   public String getScmAuthorData(String fileKey) {
     checkPermission(fileKey);
@@ -96,5 +118,4 @@ public class SourceService implements ServerComponent {
       MyBatis.closeQuietly(session);
     }
   }
-
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/ws/RawAction.java b/server/sonar-server/src/main/java/org/sonar/server/source/ws/RawAction.java
new file mode 100644 (file)
index 0000000..14b4fdd
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.source.ws;
+
+import com.google.common.io.Resources;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.source.SourceService;
+
+import java.util.List;
+
+public class RawAction implements RequestHandler {
+
+  private final SourceService sourceService;
+
+  public RawAction(SourceService sourceService) {
+    this.sourceService = sourceService;
+  }
+
+  void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction("raw")
+      .setDescription("Get source code as plain text. Require Browse permission on file's project")
+      .setSince("5.0")
+      .setResponseExample(Resources.getResource(getClass(), "example-raw.txt"))
+      .setHandler(this);
+
+    action
+      .createParam("key")
+      .setRequired(true)
+      .setDescription("File key")
+      .setExampleValue("my_project:/src/foo/Bar.php");
+  }
+
+  @Override
+  public void handle(Request request, Response response) {
+    String fileKey = request.mandatoryParam("key");
+    List<String> lines = sourceService.getLinesAsTxt(fileKey);
+    if (lines.isEmpty()) {
+      throw new NotFoundException("File '" + fileKey + "' has no sources");
+    }
+    response.newTxtWriter().values(lines).close();
+  }
+}
index b0ad99bcc66a995cef34e27150b0046c23597587..e161a6349e22d98be1460beb9e9069c268471954 100644 (file)
@@ -25,10 +25,12 @@ import org.sonar.api.server.ws.WebService;
 public class SourcesWs implements WebService {
 
   private final ShowAction showAction;
+  private final RawAction rawAction;
   private final ScmAction scmAction;
 
-  public SourcesWs(ShowAction showAction, ScmAction scmAction) {
+  public SourcesWs(ShowAction showAction, RawAction rawAction, ScmAction scmAction) {
     this.showAction = showAction;
+    this.rawAction = rawAction;
     this.scmAction = scmAction;
   }
 
@@ -38,6 +40,7 @@ public class SourcesWs implements WebService {
       .setSince("4.2")
       .setDescription("Display sources information");
     showAction.define(controller);
+    rawAction.define(controller);
     scmAction.define(controller);
     controller.done();
   }
index 909ec0c145ad4cdf69138f29e62d877c349cfdf6..161d753c2a59ba89659b64c1405d4acfc81ba067 100644 (file)
@@ -21,10 +21,12 @@ package org.sonar.server.ws;
 
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.utils.text.TxtWriter;
 import org.sonar.api.utils.text.XmlWriter;
 import org.sonar.server.plugins.MimeTypes;
 
 import javax.annotation.CheckForNull;
+
 import java.io.ByteArrayOutputStream;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
@@ -86,6 +88,12 @@ public class ServletResponse implements Response {
     return XmlWriter.of(new OutputStreamWriter(stream.output()));
   }
 
+  @Override
+  public TxtWriter newTxtWriter() {
+    stream.setMediaType(MimeTypes.TXT);
+    return TxtWriter.of(new OutputStreamWriter(stream.output()));
+  }
+
   @Override
   public ServletStream stream() {
     return stream;
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/source/ws/example-raw.txt b/server/sonar-server/src/main/resources/org/sonar/server/source/ws/example-raw.txt
new file mode 100644 (file)
index 0000000..f93f54c
--- /dev/null
@@ -0,0 +1,7 @@
+package org.sonar.check;
+
+public enum Priority {
+/**
+  * WARNING : DO NOT CHANGE THE ENUMERATION ORDER
+  * the enum ordinal is used for db persistence
+
index 27131efe1411fbba6c463ebb8f695588ca23f960..e501ede78026d732c8fd4e23ebf6a41b0f55aadb 100644 (file)
@@ -29,11 +29,14 @@ import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.measure.db.MeasureKey;
 import org.sonar.core.persistence.DbSession;
+import org.sonar.core.source.db.SnapshotSourceDao;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.measure.persistence.MeasureDao;
 import org.sonar.server.user.MockUserSession;
 
+import java.util.List;
+
 import static org.fest.assertions.Assertions.assertThat;
 import static org.fest.assertions.Fail.fail;
 import static org.mockito.Mockito.*;
@@ -47,6 +50,9 @@ public class SourceServiceTest {
   @Mock
   HtmlSourceDecorator sourceDecorator;
 
+  @Mock
+  SnapshotSourceDao snapshotSourceDao;
+
   @Mock
   DeprecatedSourceDecorator deprecatedSourceDecorator;
 
@@ -63,11 +69,11 @@ public class SourceServiceTest {
     DbClient dbClient = mock(DbClient.class);
     when(dbClient.openSession(false)).thenReturn(session);
     when(dbClient.measureDao()).thenReturn(measureDao);
-    service = new SourceService(dbClient, sourceDecorator, deprecatedSourceDecorator);
+    service = new SourceService(dbClient, sourceDecorator, snapshotSourceDao, deprecatedSourceDecorator);
   }
 
   @Test
-  public void get_lines() throws Exception {
+  public void get_html_lines() throws Exception {
     MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY);
 
     service.getLinesAsHtml(COMPONENT_KEY);
@@ -133,4 +139,24 @@ public class SourceServiceTest {
     assertThat(service.getScmDateData(COMPONENT_KEY)).isNull();
   }
 
+  @Test
+  public void get_txt_lines() throws Exception {
+    MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY);
+
+    when(snapshotSourceDao.selectSnapshotSourceByComponentKey(COMPONENT_KEY, session)).thenReturn("line1\nline2");
+
+    List<String> result = service.getLinesAsTxt(COMPONENT_KEY);
+    assertThat(result).contains("line1", "line2");
+  }
+
+  @Test
+  public void get_txt_lines_when_no_source() throws Exception {
+    MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY);
+
+    when(snapshotSourceDao.selectSnapshotSourceByComponentKey(COMPONENT_KEY, session)).thenReturn(null);
+
+    List<String> result = service.getLinesAsTxt(COMPONENT_KEY);
+    assertThat(result).isEmpty();
+  }
+
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/ws/RawActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/ws/RawActionTest.java
new file mode 100644 (file)
index 0000000..07794a4
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.source.ws;
+
+import org.junit.Test;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.source.SourceService;
+import org.sonar.server.ws.WsTester;
+
+import java.util.Collections;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RawActionTest {
+
+  SourceService sourceService = mock(SourceService.class);
+  WsTester tester = new WsTester(new SourcesWs(mock(ShowAction.class), new RawAction(sourceService), mock(ScmAction.class)));
+
+  @Test
+  public void get_txt() throws Exception {
+    String fileKey = "src/Foo.java";
+    when(sourceService.getLinesAsTxt(fileKey)).thenReturn(newArrayList(
+      "public class HelloWorld {",
+      "}"
+    ));
+
+    WsTester.TestRequest request = tester.newGetRequest("api/sources", "raw").setParam("key", fileKey);
+    String result = request.execute().outputAsString();
+    assertThat(result).isEqualTo("public class HelloWorld {\r\n}\r\n");
+  }
+
+  @Test(expected = NotFoundException.class)
+  public void fail_to_get_txt_when_no_source() throws Exception {
+    String fileKey = "src/Foo.java";
+    when(sourceService.getLinesAsTxt(fileKey)).thenReturn(Collections.<String>emptyList());
+
+    WsTester.TestRequest request = tester.newGetRequest("api/sources", "raw").setParam("key", fileKey);
+    request.execute();
+  }
+
+}
index 629c15b0fa2098974ef18340b66aa1056af05dcb..a572c19e7eff0e7322d4b5a117fa567b07fe5b89 100644 (file)
@@ -26,15 +26,13 @@ import org.sonar.server.ws.WsTester;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 public class ScmActionTest {
 
   SourceService sourceService = mock(SourceService.class);
   ScmWriter scmWriter = mock(ScmWriter.class);
-  WsTester tester = new WsTester(new SourcesWs(mock(ShowAction.class), new ScmAction(sourceService, scmWriter)));
+  WsTester tester = new WsTester(new SourcesWs(mock(ShowAction.class), mock(RawAction.class), new ScmAction(sourceService, scmWriter)));
 
   @Test
   public void get_scm() throws Exception {
index 1f21f64ee9e9879d100c5010563396736d60d7f1..ace2c100374438dbc0470cddf8e0712944e93a42 100644 (file)
@@ -41,7 +41,7 @@ public class ShowActionTest {
 
   @Before
   public void setUp() throws Exception {
-    tester = new WsTester(new SourcesWs(new ShowAction(sourceService), new ScmAction(sourceService, mock(ScmWriter.class))));
+    tester = new WsTester(new SourcesWs(new ShowAction(sourceService), mock(RawAction.class), new ScmAction(sourceService, mock(ScmWriter.class))));
   }
 
   @Test
index 3d47f925a57d711058449bf1dfcff45f9d022e76..c17546769164f42acc8d5d54964d407634b2a770 100644 (file)
@@ -31,8 +31,9 @@ import static org.mockito.Mockito.mock;
 public class SourcesWsTest {
 
   ShowAction showAction = new ShowAction(mock(SourceService.class));
+  RawAction rawAction = new RawAction(mock(SourceService.class));
   ScmAction scmAction = new ScmAction(mock(SourceService.class), new ScmWriter());
-  WsTester tester = new WsTester(new SourcesWs(showAction, scmAction));
+  WsTester tester = new WsTester(new SourcesWs(showAction, rawAction, scmAction));
 
   @Test
   public void define_ws() throws Exception {
@@ -40,6 +41,7 @@ public class SourcesWsTest {
     assertThat(controller).isNotNull();
     assertThat(controller.since()).isEqualTo("4.2");
     assertThat(controller.description()).isNotEmpty();
+    assertThat(controller.actions()).hasSize(3);
 
     WebService.Action show = controller.action("show");
     assertThat(show).isNotNull();
@@ -49,6 +51,14 @@ public class SourcesWsTest {
     assertThat(show.responseExampleAsString()).isNotEmpty();
     assertThat(show.params()).hasSize(3);
 
+    WebService.Action raw = controller.action("raw");
+    assertThat(raw).isNotNull();
+    assertThat(raw.handler()).isSameAs(rawAction);
+    assertThat(raw.since()).isEqualTo("5.0");
+    assertThat(raw.isInternal()).isFalse();
+    assertThat(raw.responseExampleAsString()).isNotEmpty();
+    assertThat(raw.params()).hasSize(1);
+
     WebService.Action scm = controller.action("scm");
     assertThat(scm).isNotNull();
     assertThat(scm.handler()).isSameAs(scmAction);
index bae37674313a2c7fc1a0d78604b00b3a9acac4d6..07d9fa3aac964655eb65d3feeacb6a033477bffc 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.internal.ValidatingRequest;
 import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.utils.text.TxtWriter;
 import org.sonar.api.utils.text.XmlWriter;
 import org.sonar.server.ws.WsTester.TestResponse.TestStream;
 
@@ -132,6 +133,11 @@ public class WsTester {
       return XmlWriter.of(new OutputStreamWriter(output, Charsets.UTF_8));
     }
 
+    @Override
+    public TxtWriter newTxtWriter() {
+      return TxtWriter.of(new OutputStreamWriter(output, Charsets.UTF_8));
+    }
+
     @Override
     public Stream stream() {
       if (stream == null) {
index 149ae4178c74b23ab935c927b2169479b6bedae0..b192078a057ec78f5610361076cc24f524a2402c 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.api.server.ws;
 
 import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.utils.text.TxtWriter;
 import org.sonar.api.utils.text.XmlWriter;
 
 import java.io.OutputStream;
@@ -41,6 +42,8 @@ public interface Response {
 
   XmlWriter newXmlWriter();
 
+  TxtWriter newTxtWriter();
+
   Response noContent();
 
   Stream stream();
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/text/TxtWriter.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/text/TxtWriter.java
new file mode 100644 (file)
index 0000000..e52f92a
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.api.utils.text;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * @since 5.0
+ */
+public class TxtWriter {
+
+  private final Writer writer;
+  private static final String LINE_SEPARATOR = "\r\n";
+
+  private TxtWriter(Writer writer) {
+    this.writer = writer;
+  }
+
+  public static TxtWriter of(Writer writer) {
+    return new TxtWriter(writer);
+  }
+
+  public TxtWriter values(List<String> values) {
+    for (String value : values) {
+      write(value);
+      write(LINE_SEPARATOR);
+    }
+    return this;
+  }
+
+  public TxtWriter values(String... values) {
+    return values(newArrayList(values));
+  }
+
+  public void close() {
+    try {
+      writer.close();
+    } catch (IOException e) {
+      throw new WriterException("Fail to close TXT output", e);
+    }
+  }
+
+  private void write(String s) {
+    try {
+      writer.append(s);
+    } catch (IOException e) {
+      throw new WriterException("Fail to generate TXT with value: " + s, e);
+    }
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/text/TxtWriterTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/text/TxtWriterTest.java
new file mode 100644 (file)
index 0000000..dca31b0
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.api.utils.text;
+
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class TxtWriterTest {
+
+  @Test
+  public void write_txt() throws Exception {
+    StringWriter output = new StringWriter();
+    TxtWriter writer = TxtWriter.of(output);
+
+    writer.values("France", "Paris");
+    writer.close();
+
+    BufferedReader reader = new BufferedReader(new StringReader(output.toString()));
+    String line1 = reader.readLine();
+    assertThat(line1).isEqualTo("France");
+
+    String line2 = reader.readLine();
+    assertThat(line2).isEqualTo("Paris");
+
+    assertThat(reader.readLine()).isNull();
+  }
+
+}