diff options
8 files changed, 302 insertions, 26 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java index 3f011e63148..5c1d75a8590 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java @@ -29,6 +29,7 @@ import org.sonar.core.persistence.MyBatis; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -52,12 +53,27 @@ public class ResourceDao { } } + public List<ResourceDto> getResources(ResourceQuery query, SqlSession session) { + return session.getMapper(ResourceMapper.class).selectResources(query); + } + /** * Return a single result or null. If the request returns multiple rows, then * the first row is returned. */ + @CheckForNull public ResourceDto getResource(ResourceQuery query) { - List<ResourceDto> resources = getResources(query); + SqlSession session = mybatis.openSession(); + try { + return getResource(query, session); + } finally { + MyBatis.closeQuietly(session); + } + } + + @CheckForNull + public ResourceDto getResource(ResourceQuery query, SqlSession session) { + List<ResourceDto> resources = getResources(query, session); if (!resources.isEmpty()) { return resources.get(0); } diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index a5f559743b6..90a071daa43 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -97,6 +97,7 @@ import org.sonar.server.plugins.*; import org.sonar.server.qualityprofile.*; import org.sonar.server.rule.*; import org.sonar.server.rule.ws.*; +import org.sonar.server.source.DeprecatedSourceDecorator; import org.sonar.server.source.HtmlSourceDecorator; import org.sonar.server.source.SourceService; import org.sonar.server.source.ws.SourcesShowWsHandler; @@ -370,6 +371,7 @@ public final class Platform { // source servicesContainer.addSingleton(HtmlSourceDecorator.class); + servicesContainer.addSingleton(DeprecatedSourceDecorator.class); servicesContainer.addSingleton(SourceService.class); servicesContainer.addSingleton(SourcesWs.class); servicesContainer.addSingleton(SourcesShowWsHandler.class); diff --git a/sonar-server/src/main/java/org/sonar/server/source/DeprecatedSourceDecorator.java b/sonar-server/src/main/java/org/sonar/server/source/DeprecatedSourceDecorator.java new file mode 100644 index 00000000000..352d7df4d91 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/source/DeprecatedSourceDecorator.java @@ -0,0 +1,95 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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; + +import com.google.common.base.Splitter; +import org.apache.ibatis.session.SqlSession; +import org.sonar.api.ServerComponent; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.resource.ResourceDao; +import org.sonar.core.resource.ResourceDto; +import org.sonar.core.resource.ResourceQuery; +import org.sonar.core.source.db.SnapshotSourceDao; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.ui.CodeColorizers; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; + +/** + * When a plugin do not use the new API to add syntax highlighting on source code, this class is called to add html info on source code + */ +public class DeprecatedSourceDecorator implements ServerComponent { + + private final MyBatis mybatis; + + private final ResourceDao resourceDao; + private final CodeColorizers codeColorizers; + private final SnapshotSourceDao snapshotSourceDao; + + public DeprecatedSourceDecorator(MyBatis mybatis, ResourceDao resourceDao, CodeColorizers codeColorizers, SnapshotSourceDao snapshotSourceDao) { + this.mybatis = mybatis; + this.resourceDao = resourceDao; + this.codeColorizers = codeColorizers; + this.snapshotSourceDao = snapshotSourceDao; + } + + @CheckForNull + public List<String> getSourceAsHtml(String componentKey) { + return getSourceAsHtml(componentKey, null, null); + } + + @CheckForNull + public List<String> getSourceAsHtml(String componentKey, @Nullable Integer from, @Nullable Integer to) { + SqlSession session = mybatis.openSession(); + try { + ResourceDto component = resourceDao.getResource(ResourceQuery.create().setKey(componentKey), session); + if (component == null) { + throw new NotFoundException("The component '" + componentKey + "' does not exists."); + } + String source = snapshotSourceDao.selectSnapshotSourceByComponentKey(componentKey, session); + if (source != null) { + return splitSourceByLine(source, component.getLanguage(), from, to); + } else { + return null; + } + } finally { + MyBatis.closeQuietly(session); + } + } + + private List<String> splitSourceByLine(String source, String language, @Nullable Integer from, @Nullable Integer to) { + String htmlSource = codeColorizers.toHtml(source, language); + List<String> splitSource = newArrayList(Splitter.onPattern("\r?\n|\r").split(htmlSource)); + List<String> result = newArrayList(); + for (int i = 0; i < splitSource.size(); i++) { + int currentLine = i+1; + if ((from == null || currentLine >= from) && (to == null || to >= currentLine)) { + result.add(splitSource.get(currentLine - 1)); + } + } + return result; + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/source/SourceService.java b/sonar-server/src/main/java/org/sonar/server/source/SourceService.java index ab17c9e4419..3d04cb4fa99 100644 --- a/sonar-server/src/main/java/org/sonar/server/source/SourceService.java +++ b/sonar-server/src/main/java/org/sonar/server/source/SourceService.java @@ -28,7 +28,6 @@ import org.sonar.core.measure.db.MeasureDataDto; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.ui.CodeColorizers; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; @@ -38,35 +37,40 @@ import java.util.List; public class SourceService implements ServerComponent { - private final HtmlSourceDecorator sourceDecorator; /** * Old service to colorize code */ - private final CodeColorizers codeColorizers; + private final DeprecatedSourceDecorator deprecatedSourceDecorator; private final ResourceDao resourceDao; private final MeasureDataDao measureDataDao; - public SourceService(HtmlSourceDecorator sourceDecorator, CodeColorizers codeColorizers, ResourceDao resourceDao, MeasureDataDao measureDataDao) { + public SourceService(HtmlSourceDecorator sourceDecorator, DeprecatedSourceDecorator deprecatedSourceDecorator, ResourceDao resourceDao, MeasureDataDao measureDataDao) { this.sourceDecorator = sourceDecorator; - this.codeColorizers = codeColorizers; + this.deprecatedSourceDecorator = deprecatedSourceDecorator; this.resourceDao = resourceDao; this.measureDataDao = measureDataDao; } - public List<String> sourcesFromComponent(String componentKey) { - return sourcesFromComponent(componentKey, null, null); + public List<String> getSourcesForComponent(String componentKey) { + return getSourcesByComponent(componentKey, null, null); } - public List<String> sourcesFromComponent(String componentKey, @Nullable Integer from, @Nullable Integer to) { + public List<String> getSourcesByComponent(String componentKey, @Nullable Integer from, @Nullable Integer to) { ResourceDto project = resourceDao.getRootProjectByComponentKey(componentKey); if (project == null) { throw new NotFoundException("This component does not exists."); } UserSession.get().checkProjectPermission(UserRole.CODEVIEWER, project.getKey()); - return sourceDecorator.getDecoratedSourceAsHtml(componentKey, from, to); + + List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml(componentKey, from, to); + if (decoratedSource != null) { + return decoratedSource; + } else { + return deprecatedSourceDecorator.getSourceAsHtml(componentKey, from, to); + } } @CheckForNull diff --git a/sonar-server/src/main/java/org/sonar/server/source/ws/SourcesShowWsHandler.java b/sonar-server/src/main/java/org/sonar/server/source/ws/SourcesShowWsHandler.java index b4ee35ac3da..ca1dcb909d3 100644 --- a/sonar-server/src/main/java/org/sonar/server/source/ws/SourcesShowWsHandler.java +++ b/sonar-server/src/main/java/org/sonar/server/source/ws/SourcesShowWsHandler.java @@ -46,7 +46,7 @@ public class SourcesShowWsHandler implements RequestHandler { String componentKey = request.requiredParam("key"); Integer fromParam = request.intParam("from"); Integer toParam = request.intParam("to"); - List<String> sourceHtml = sourceService.sourcesFromComponent(componentKey, fromParam, toParam); + List<String> sourceHtml = sourceService.getSourcesByComponent(componentKey, fromParam, toParam); if (sourceHtml == null) { throw new NotFoundException("Component : " + componentKey + " has no source."); } diff --git a/sonar-server/src/test/java/org/sonar/server/source/DeprecatedSourceDecoratorTest.java b/sonar-server/src/test/java/org/sonar/server/source/DeprecatedSourceDecoratorTest.java new file mode 100644 index 00000000000..d08f48393ee --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/source/DeprecatedSourceDecoratorTest.java @@ -0,0 +1,147 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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; + +import org.apache.ibatis.session.SqlSession; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.resource.ResourceDao; +import org.sonar.core.resource.ResourceDto; +import org.sonar.core.resource.ResourceQuery; +import org.sonar.core.source.db.SnapshotSourceDao; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.ui.CodeColorizers; + +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class DeprecatedSourceDecoratorTest { + + @Mock + MyBatis mybatis; + + @Mock + SqlSession session; + + @Mock + ResourceDao resourceDao; + + @Mock + CodeColorizers codeColorizers; + + @Mock + SnapshotSourceDao snapshotSourceDao; + + DeprecatedSourceDecorator sourceDecorator; + + @Before + public void setUp() throws Exception { + when(mybatis.openSession()).thenReturn(session); + sourceDecorator = new DeprecatedSourceDecorator(mybatis, resourceDao, codeColorizers, snapshotSourceDao); + } + + @Test + public void get_source_as_html() throws Exception { + String componentKey = "org.sonar.sample:Sample"; + String source = "line 1\nline 2\nline 3\n"; + String htmlSource = "<span>line 1</span>\n<span>line 2</span>\n"; + + when(resourceDao.getResource(any(ResourceQuery.class), eq(session))).thenReturn(new ResourceDto().setKey(componentKey).setLanguage("java")); + when(snapshotSourceDao.selectSnapshotSourceByComponentKey(componentKey, session)).thenReturn(source); + when(codeColorizers.toHtml(source, "java")).thenReturn(htmlSource); + + List<String> result = sourceDecorator.getSourceAsHtml(componentKey); + assertThat(result).containsExactly("<span>line 1</span>", "<span>line 2</span>", ""); + } + + @Test + public void return_null_if_no_source_code_on_component() throws Exception { + String componentKey = "org.sonar.sample:Sample"; + when(resourceDao.getResource(any(ResourceQuery.class), eq(session))).thenReturn(new ResourceDto().setKey(componentKey).setLanguage("java")); + when(snapshotSourceDao.selectSnapshotSourceByComponentKey(componentKey, session)).thenReturn(null); + + assertThat(sourceDecorator.getSourceAsHtml(componentKey)).isNull(); + } + + @Test + public void fail_to_get_source_as_html_on_unknown_component() throws Exception { + String componentKey = "org.sonar.sample:Sample"; + when(resourceDao.getResource(any(ResourceQuery.class), eq(session))).thenReturn(null); + try { + sourceDecorator.getSourceAsHtml(componentKey); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(NotFoundException.class); + } + } + + @Test + public void get_source_as_html_with_from_and_to_params() throws Exception { + String componentKey = "org.sonar.sample:Sample"; + String source = "line 1\nline 2\nline 3\n"; + String htmlSource = "<span>line 1</span>\n<span>line 2</span>\n<span>line 3</span>\n"; + + when(resourceDao.getResource(any(ResourceQuery.class), eq(session))).thenReturn(new ResourceDto().setKey(componentKey).setLanguage("java")); + when(snapshotSourceDao.selectSnapshotSourceByComponentKey(componentKey, session)).thenReturn(source); + when(codeColorizers.toHtml(source, "java")).thenReturn(htmlSource); + + List<String> result = sourceDecorator.getSourceAsHtml(componentKey, 2, 3); + assertThat(result).containsExactly("<span>line 2</span>", "<span>line 3</span>"); + } + + @Test + public void get_source_as_html_with_from_param() throws Exception { + String componentKey = "org.sonar.sample:Sample"; + String source = "line 1\nline 2\nline 3\n"; + String htmlSource = "<span>line 1</span>\n<span>line 2</span>\n<span>line 3</span>\n"; + + when(resourceDao.getResource(any(ResourceQuery.class), eq(session))).thenReturn(new ResourceDto().setKey(componentKey).setLanguage("java")); + when(snapshotSourceDao.selectSnapshotSourceByComponentKey(componentKey, session)).thenReturn(source); + when(codeColorizers.toHtml(source, "java")).thenReturn(htmlSource); + + List<String> result = sourceDecorator.getSourceAsHtml(componentKey, 2, null); + assertThat(result).containsExactly("<span>line 2</span>", "<span>line 3</span>", ""); + } + + @Test + public void get_source_as_html_with_to_param() throws Exception { + String componentKey = "org.sonar.sample:Sample"; + String source = "line 1\nline 2\nline 3\n"; + String htmlSource = "<span>line 1</span>\n<span>line 2</span>\n<span>line 3</span>\n"; + + when(resourceDao.getResource(any(ResourceQuery.class), eq(session))).thenReturn(new ResourceDto().setKey(componentKey).setLanguage("java")); + when(snapshotSourceDao.selectSnapshotSourceByComponentKey(componentKey, session)).thenReturn(source); + when(codeColorizers.toHtml(source, "java")).thenReturn(htmlSource); + + List<String> result = sourceDecorator.getSourceAsHtml(componentKey, null, 3); + assertThat(result).containsExactly("<span>line 1</span>", "<span>line 2</span>", "<span>line 3</span>"); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java b/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java index 69728a01a49..4bce70a3394 100644 --- a/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java @@ -31,7 +31,6 @@ import org.sonar.core.measure.db.MeasureDataDao; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.ui.CodeColorizers; import org.sonar.server.user.MockUserSession; import static org.fest.assertions.Assertions.assertThat; @@ -45,7 +44,7 @@ public class SourceServiceTest { HtmlSourceDecorator sourceDecorator; @Mock - CodeColorizers codeColorizers; + DeprecatedSourceDecorator deprecatedSourceDecorator;; @Mock ResourceDao resourceDao; @@ -57,30 +56,30 @@ public class SourceServiceTest { @Before public void setUp() throws Exception { - service = new SourceService(sourceDecorator, codeColorizers, resourceDao, measureDataDao); + service = new SourceService(sourceDecorator, deprecatedSourceDecorator, resourceDao, measureDataDao); } @Test - public void sources_from_component() throws Exception { + public void get_sources_by_component() throws Exception { String projectKey = "org.sonar.sample"; String componentKey = "org.sonar.sample:Sample"; MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, projectKey); when(resourceDao.getRootProjectByComponentKey(componentKey)).thenReturn(new ResourceDto().setKey(projectKey)); - service.sourcesFromComponent(componentKey); + service.getSourcesForComponent(componentKey); verify(sourceDecorator).getDecoratedSourceAsHtml(componentKey, null, null); } @Test - public void fail_to_get_sources_from_component_if_component_not_found() throws Exception { + public void fail_to_get_sources_by_component_if_component_not_found() throws Exception { String projectKey = "org.sonar.sample"; String componentKey = "org.sonar.sample:Sample"; MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, projectKey); when(resourceDao.getRootProjectByComponentKey(componentKey)).thenReturn(null); try { - service.sourcesFromComponent(componentKey); + service.getSourcesForComponent(componentKey); fail(); } catch (Exception e) { assertThat(e).isInstanceOf(NotFoundException.class); @@ -90,18 +89,31 @@ public class SourceServiceTest { } @Test - public void sources_from_component_with_only_given_lines() throws Exception { + public void get_sources_by_component_with_only_given_lines() throws Exception { String projectKey = "org.sonar.sample"; String componentKey = "org.sonar.sample:Sample"; MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, projectKey); when(resourceDao.getRootProjectByComponentKey(componentKey)).thenReturn(new ResourceDto().setKey(projectKey)); - service.sourcesFromComponent(componentKey, 1, 2); + service.getSourcesByComponent(componentKey, 1, 2); verify(sourceDecorator).getDecoratedSourceAsHtml(componentKey, 1, 2); } @Test + public void get_sources_by_component_from_deprecated_source_decorator_when_no_data_from_new_decorator() throws Exception { + String projectKey = "org.sonar.sample"; + String componentKey = "org.sonar.sample:Sample"; + MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, projectKey); + when(resourceDao.getRootProjectByComponentKey(componentKey)).thenReturn(new ResourceDto().setKey(projectKey)); + when(sourceDecorator.getDecoratedSourceAsHtml(eq(componentKey), anyInt(), anyInt())).thenReturn(null); + + service.getSourcesByComponent(componentKey, 1, 2); + + verify(deprecatedSourceDecorator).getSourceAsHtml(componentKey, 1, 2); + } + + @Test public void get_scm_author_data() throws Exception { String componentKey = "org.sonar.sample:Sample"; service.getScmAuthorData(componentKey); diff --git a/sonar-server/src/test/java/org/sonar/server/source/ws/SourcesShowWsHandlerTest.java b/sonar-server/src/test/java/org/sonar/server/source/ws/SourcesShowWsHandlerTest.java index 0410653bb80..d599cfa8b3e 100644 --- a/sonar-server/src/test/java/org/sonar/server/source/ws/SourcesShowWsHandlerTest.java +++ b/sonar-server/src/test/java/org/sonar/server/source/ws/SourcesShowWsHandlerTest.java @@ -51,7 +51,7 @@ public class SourcesShowWsHandlerTest { @Test public void show_source() throws Exception { String componentKey = "org.apache.struts:struts:Dispatcher"; - when(sourceService.sourcesFromComponent(eq(componentKey), anyInt(), anyInt())).thenReturn(newArrayList( + when(sourceService.getSourcesByComponent(eq(componentKey), anyInt(), anyInt())).thenReturn(newArrayList( "/*", " * Header", " */", @@ -67,7 +67,7 @@ public class SourcesShowWsHandlerTest { @Test public void fail_to_show_source_if_no_source_found() throws Exception { String componentKey = "org.apache.struts:struts:Dispatcher"; - when(sourceService.sourcesFromComponent(anyString(), anyInt(), anyInt())).thenReturn(null); + when(sourceService.getSourcesByComponent(anyString(), anyInt(), anyInt())).thenReturn(null); try { WsTester.TestRequest request = tester.newRequest("show").setParam("key", componentKey); @@ -81,7 +81,7 @@ public class SourcesShowWsHandlerTest { @Test public void show_source_with_from_and_to_params() throws Exception { String componentKey = "org.apache.struts:struts:Dispatcher"; - when(sourceService.sourcesFromComponent(componentKey, 3, 5)).thenReturn(newArrayList( + when(sourceService.getSourcesByComponent(componentKey, 3, 5)).thenReturn(newArrayList( " */", "", "public class <span class=\"sym-31 sym\">HelloWorld</span> {" @@ -93,7 +93,7 @@ public class SourcesShowWsHandlerTest { @Test public void show_source_with_scm() throws Exception { String componentKey = "org.apache.struts:struts:Dispatcher"; - when(sourceService.sourcesFromComponent(eq(componentKey), anyInt(), anyInt())).thenReturn(newArrayList( + when(sourceService.getSourcesByComponent(eq(componentKey), anyInt(), anyInt())).thenReturn(newArrayList( "public class <span class=\"sym-31 sym\">HelloWorld</span> {}" )); @@ -107,7 +107,7 @@ public class SourcesShowWsHandlerTest { @Test public void show_source_with_scm_with_from_and_to_params() throws Exception { String componentKey = "org.apache.struts:struts:Dispatcher"; - when(sourceService.sourcesFromComponent(componentKey, 3, 5)).thenReturn(newArrayList( + when(sourceService.getSourcesByComponent(componentKey, 3, 5)).thenReturn(newArrayList( " */", "", "public class <span class=\"sym-31 sym\">HelloWorld</span> {" @@ -124,7 +124,7 @@ public class SourcesShowWsHandlerTest { @Test public void show_source_with_scm_without_repeating_same_lines() throws Exception { String componentKey = "org.apache.struts:struts:Dispatcher"; - when(sourceService.sourcesFromComponent(eq(componentKey), anyInt(), anyInt())).thenReturn(newArrayList( + when(sourceService.getSourcesByComponent(eq(componentKey), anyInt(), anyInt())).thenReturn(newArrayList( " */", "", "public class <span class=\"sym-31 sym\">HelloWorld</span> {" |