diff options
author | Julien Lancelot <julien.lancelot@gmail.com> | 2013-05-22 15:13:36 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@gmail.com> | 2013-05-22 15:13:45 +0200 |
commit | 3342b4980ff6e0f5a4d96ec00e311b2141c1eff3 (patch) | |
tree | 75444a525f107817cb0aa8e05b3f9e73eaa09522 /sonar-server | |
parent | 7dc0d68a5a3cab41afd5dc1c814a6ed4d4900666 (diff) | |
download | sonarqube-3342b4980ff6e0f5a4d96ec00e311b2141c1eff3.tar.gz sonarqube-3342b4980ff6e0f5a4d96ec00e311b2141c1eff3.zip |
SONAR-4301 Replace sql sort on issues by Java sort
Diffstat (limited to 'sonar-server')
4 files changed, 368 insertions, 25 deletions
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java index caea62016a2..1ce627cc59f 100644 --- a/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java +++ b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java @@ -106,12 +106,17 @@ public class DefaultIssueFinder implements IssueFinder { // 2. Select the authorized ids of all the issues that match the query List<IssueDto> authorizedIssues = issueDao.selectIssueAndProjectIds(query, rootProjectIds, sqlSession); - // 3. Apply pagination - Paging paging = Paging.create(query.pageSize(), query.pageIndex(), authorizedIssues.size()); - Set<Long> pagedIssueIds = pagedIssueIds(authorizedIssues, paging); + // 3. Sort all authorized issues + Collection<IssueDto> authorizedSortedIssues = new IssuesFinderSort(authorizedIssues, query).sort(); + + // 4. Apply pagination + Paging paging = Paging.create(query.pageSize(), query.pageIndex(), authorizedSortedIssues.size()); + Set<Long> pagedIssueIds = pagedIssueIds(authorizedSortedIssues, paging); + + // 5. Load issues and their related data (rules, components, projects, comments, action plans, ...) and sort then again + Collection<IssueDto> pagedIssues = issueDao.selectByIds(pagedIssueIds, sqlSession); + Collection<IssueDto> pagedSortedIssues = new IssuesFinderSort(pagedIssues, query).sort(); - // 4. Load issues and their related data (rules, components, comments, action plans, ...) - Collection<IssueDto> pagedIssues = issueDao.selectByIds(pagedIssueIds, query.sort(), query.asc(), sqlSession); Map<String, DefaultIssue> issuesByKey = newHashMap(); List<Issue> issues = newArrayList(); Set<Integer> ruleIds = Sets.newHashSet(); @@ -119,7 +124,7 @@ public class DefaultIssueFinder implements IssueFinder { Set<Integer> projectIds = Sets.newHashSet(); Set<String> actionPlanKeys = Sets.newHashSet(); Set<String> users = Sets.newHashSet(); - for (IssueDto dto : pagedIssues) { + for (IssueDto dto : pagedSortedIssues) { DefaultIssue defaultIssue = dto.toDefaultIssue(); issuesByKey.put(dto.getKee(), defaultIssue); issues.add(defaultIssue); @@ -144,15 +149,15 @@ public class DefaultIssueFinder implements IssueFinder { } return new DefaultResults(issues, - findRules(ruleIds), - findComponents(componentIds), - findProjects(projectIds), - findActionPlans(actionPlanKeys), - findUsers(users), - paging, - false, - authorizedIssues.size() != query.maxResults() - // TODO + findRules(ruleIds), + findComponents(componentIds), + findProjects(projectIds), + findActionPlans(actionPlanKeys), + findUsers(users), + paging, + false, + authorizedIssues.size() != query.maxResults() + // TODO // authorizedIssues.size() != allIssues.size() ); } finally { diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssuesFinderSort.java b/sonar-server/src/main/java/org/sonar/server/issue/IssuesFinderSort.java new file mode 100644 index 00000000000..086365f974d --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/issue/IssuesFinderSort.java @@ -0,0 +1,181 @@ +/* + * 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.issue; + +import com.google.common.base.Function; +import com.google.common.collect.Ordering; +import org.sonar.api.issue.IssueQuery; +import org.sonar.api.rule.Severity; +import org.sonar.core.issue.db.IssueDto; + +import java.util.Collection; +import java.util.Date; +import java.util.List; + +class IssuesFinderSort { + + private Collection<IssueDto> issues; + private IssueQuery query; + + public IssuesFinderSort(Collection<IssueDto> issues, IssueQuery query) { + this.issues = issues; + this.query = query; + } + + public Collection<IssueDto> sort() { + if (query.sort() != null) { + IssueProcessor issueProcessor; + switch (query.sort()) { + case ASSIGNEE: + issueProcessor = new AssigneeSortIssueProcessor(); + break; + case SEVERITY: + issueProcessor = new SeveritySortIssueProcessor(); + break; + case STATUS: + issueProcessor = new StatusSortIssueProcessor(); + break; + case CREATION_DATE: + issueProcessor = new CreationDateSortIssueProcessor(); + break; + case UPDATE_DATE: + issueProcessor = new UpdateDateSortIssueProcessor(); + break; + case CLOSE_DATE: + issueProcessor = new CloseDateSortIssueProcessor(); + break; + default: + throw new IllegalArgumentException("Cannot sort issues on field : " + query.sort().name()); + } + return issueProcessor.sort(issues, query.asc()); + } + return issues; + } + + abstract static class IssueProcessor { + abstract Function sortFieldFunction(); + + abstract Ordering sortFieldOrdering(boolean ascending); + + final List<IssueDto> sort(Collection<IssueDto> issueDtos, boolean ascending) { + Ordering<IssueDto> ordering = sortFieldOrdering(ascending).onResultOf(sortFieldFunction()); + return ordering.immutableSortedCopy(issueDtos); + } + } + + abstract static class TextSortIssueProcessor extends IssueProcessor { + @Override + Function sortFieldFunction() { + return new Function<IssueDto, String>() { + public String apply(IssueDto issueDto) { + return sortField(issueDto); + } + }; + } + + abstract String sortField(IssueDto issueDto); + + @Override + Ordering sortFieldOrdering(boolean ascending) { + Ordering<String> ordering = Ordering.from(String.CASE_INSENSITIVE_ORDER).nullsLast(); + if (!ascending) { + ordering = ordering.reverse(); + } + return ordering; + } + } + + static class AssigneeSortIssueProcessor extends TextSortIssueProcessor { + @Override + String sortField(IssueDto issueDto) { + return issueDto.getAssignee(); + } + } + + static class StatusSortIssueProcessor extends TextSortIssueProcessor { + @Override + String sortField(IssueDto issueDto) { + return issueDto.getStatus(); + } + } + + static class SeveritySortIssueProcessor extends IssueProcessor { + @Override + Function sortFieldFunction() { + return new Function<IssueDto, Integer>() { + public Integer apply(IssueDto issueDto) { + return Severity.ALL.indexOf(issueDto.getSeverity()); + } + }; + } + + @Override + Ordering sortFieldOrdering(boolean ascending) { + Ordering<Integer> ordering = Ordering.<Integer>natural().nullsLast(); + if (!ascending) { + ordering = ordering.reverse(); + } + return ordering; + } + } + + abstract static class DateSortRowProcessor extends IssueProcessor { + @Override + Function sortFieldFunction() { + return new Function<IssueDto, Date>() { + public Date apply(IssueDto issueDto) { + return sortField(issueDto); + } + }; + } + + abstract Date sortField(IssueDto issueDto); + + @Override + Ordering sortFieldOrdering(boolean ascending) { + Ordering<Date> ordering = Ordering.<Date>natural().nullsLast(); + if (!ascending) { + ordering = ordering.reverse(); + } + return ordering; + } + } + + static class CreationDateSortIssueProcessor extends DateSortRowProcessor { + @Override + Date sortField(IssueDto issueDto) { + return issueDto.getIssueCreationDate(); + } + } + + static class UpdateDateSortIssueProcessor extends DateSortRowProcessor { + @Override + Date sortField(IssueDto issueDto) { + return issueDto.getIssueUpdateDate(); + } + } + + static class CloseDateSortIssueProcessor extends DateSortRowProcessor { + @Override + Date sortField(IssueDto issueDto) { + return issueDto.getIssueCloseDate(); + } + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java b/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java index b2dc2fcfe90..fbb71cc901b 100644 --- a/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java +++ b/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java @@ -50,7 +50,6 @@ import static org.mockito.Matchers.anyCollection; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.*; public class DefaultIssueFinderTest { @@ -82,7 +81,7 @@ public class DefaultIssueFinderTest { .setRuleKey_unit_test_only("squid", "AvoidCycle") .setStatus("OPEN").setResolution("OPEN"); List<IssueDto> dtoList = newArrayList(issue1, issue2); - when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList); + when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList); IssueQueryResult results = finder.find(query); verify(issueDao).selectIssueAndProjectIds(eq(query), eq(newHashSet(100)), any(SqlSession.class)); @@ -106,7 +105,7 @@ public class DefaultIssueFinderTest { .setProjectKey_unit_test_only("struts") .setRuleKey_unit_test_only("squid", "AvoidCycle") .setStatus("OPEN").setResolution("OPEN"); - when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(newArrayList(issue1)); + when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(newArrayList(issue1)); finder.find(query); verify(issueDao).selectIssueAndProjectIds(eq(query), eq(Collections.<Integer>emptySet()), any(SqlSession.class)); @@ -134,7 +133,7 @@ public class DefaultIssueFinderTest { .setStatus("OPEN").setResolution("OPEN"); List<IssueDto> dtoList = newArrayList(issue1, issue2); when(issueDao.selectIssueAndProjectIds(eq(query), eq(newHashSet(100)), any(SqlSession.class))).thenReturn(dtoList); - when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList); + when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList); IssueQueryResult results = finder.find(query); assertThat(results.paging().offset()).isEqualTo(0); @@ -142,7 +141,7 @@ public class DefaultIssueFinderTest { assertThat(results.paging().pages()).isEqualTo(2); // Only one result is expected because the limit is 1 - verify(issueDao).selectByIds(eq(newHashSet(1L)), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class)); + verify(issueDao).selectByIds(eq(newHashSet(1L)), any(SqlSession.class)); } @Test @@ -178,7 +177,7 @@ public class DefaultIssueFinderTest { .setRuleKey_unit_test_only("squid", "AvoidCycle") .setStatus("OPEN").setResolution("OPEN"); List<IssueDto> dtoList = newArrayList(issue1, issue2); - when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList); + when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList); IssueQueryResult results = finder.find(query); assertThat(results.issues()).hasSize(2); @@ -206,7 +205,7 @@ public class DefaultIssueFinderTest { .setRuleKey_unit_test_only("squid", "AvoidCycle") .setStatus("OPEN").setResolution("OPEN"); List<IssueDto> dtoList = newArrayList(issue1, issue2); - when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList); + when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList); IssueQueryResult results = finder.find(query); assertThat(results.issues()).hasSize(2); @@ -233,7 +232,7 @@ public class DefaultIssueFinderTest { .setRuleKey_unit_test_only("squid", "AvoidCycle") .setStatus("OPEN").setResolution("OPEN"); List<IssueDto> dtoList = newArrayList(issue1, issue2); - when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList); + when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList); IssueQueryResult results = finder.find(query); assertThat(results.issues()).hasSize(2); @@ -260,7 +259,7 @@ public class DefaultIssueFinderTest { .setRuleKey_unit_test_only("squid", "AvoidCycle") .setStatus("OPEN").setResolution("OPEN"); List<IssueDto> dtoList = newArrayList(issue1, issue2); - when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList); + when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList); when(actionPlanService.findByKeys(anyCollection())).thenReturn(newArrayList(actionPlan1, actionPlan2)); IssueQueryResult results = finder.find(query); @@ -275,7 +274,7 @@ public class DefaultIssueFinderTest { grantAccessRights(); IssueQuery query = IssueQuery.builder().build(); when(issueDao.selectIssueAndProjectIds(eq(query), anyCollection(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList()); - when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList()); + when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList()); IssueQueryResult results = finder.find(query); assertThat(results.issues()).isEmpty(); diff --git a/sonar-server/src/test/java/org/sonar/server/issue/IssuesFinderSortTest.java b/sonar-server/src/test/java/org/sonar/server/issue/IssuesFinderSortTest.java new file mode 100644 index 00000000000..d7a458965fd --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/issue/IssuesFinderSortTest.java @@ -0,0 +1,158 @@ +/* + * 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.issue; + +import org.apache.commons.lang.time.DateUtils; +import org.junit.Test; +import org.sonar.api.issue.IssueQuery; +import org.sonar.core.issue.db.IssueDto; + +import java.util.Date; +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; +import static org.fest.assertions.Assertions.assertThat; + +public class IssuesFinderSortTest { + + @Test + public void should_sort_by_assignee() { + IssueDto issue1 = new IssueDto().setId(1L).setAssignee("perceval"); + IssueDto issue2 = new IssueDto().setId(2L).setAssignee("arthur"); + IssueDto issue3 = new IssueDto().setId(3L).setAssignee("vincent"); + IssueDto issue4 = new IssueDto().setId(4L).setAssignee(null); + List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3, issue4); + + IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.ASSIGNEE).asc(true).build(); + IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query); + + List<IssueDto> result = newArrayList(issuesFinderSort.sort()); + + assertThat(result).hasSize(4); + assertThat(result.get(0).getAssignee()).isEqualTo("arthur"); + assertThat(result.get(1).getAssignee()).isEqualTo("perceval"); + assertThat(result.get(2).getAssignee()).isEqualTo("vincent"); + assertThat(result.get(3).getAssignee()).isNull(); + } + + @Test + public void should_sort_by_status() { + IssueDto issue1 = new IssueDto().setId(1L).setStatus("CLOSED"); + IssueDto issue2 = new IssueDto().setId(2L).setStatus("REOPENED"); + IssueDto issue3 = new IssueDto().setId(3L).setStatus("OPEN"); + List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3); + + IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.STATUS).asc(false).build(); + IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query); + + List<IssueDto> result = newArrayList(issuesFinderSort.sort()); + + assertThat(result).hasSize(3); + assertThat(result.get(0).getStatus()).isEqualTo("REOPENED"); + assertThat(result.get(1).getStatus()).isEqualTo("OPEN"); + assertThat(result.get(2).getStatus()).isEqualTo("CLOSED"); + } + + @Test + public void should_sort_by_severity() { + IssueDto issue1 = new IssueDto().setId(1L).setSeverity("INFO"); + IssueDto issue2 = new IssueDto().setId(2L).setSeverity("BLOCKER"); + IssueDto issue3 = new IssueDto().setId(3L).setSeverity("MAJOR"); + List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3); + + IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.SEVERITY).asc(true).build(); + IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query); + + List<IssueDto> result = newArrayList(issuesFinderSort.sort()); + + assertThat(result).hasSize(3); + assertThat(result.get(0).getSeverity()).isEqualTo("INFO"); + assertThat(result.get(1).getSeverity()).isEqualTo("MAJOR"); + assertThat(result.get(2).getSeverity()).isEqualTo("BLOCKER"); + } + + @Test + public void should_sort_by_creation_date() { + Date date = new Date(); + Date date1 = DateUtils.addDays(date, -3); + Date date2 = DateUtils.addDays(date, -2); + Date date3 = DateUtils.addDays(date, -1); + IssueDto issue1 = new IssueDto().setId(1L).setIssueCreationDate(date1); + IssueDto issue2 = new IssueDto().setId(2L).setIssueCreationDate(date3); + IssueDto issue3 = new IssueDto().setId(3L).setIssueCreationDate(date2); + List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3); + + IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.CREATION_DATE).asc(false).build(); + IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query); + + List<IssueDto> result = newArrayList(issuesFinderSort.sort()); + + assertThat(result).hasSize(3); + assertThat(result.get(0).getIssueCreationDate()).isEqualTo(date3); + assertThat(result.get(1).getIssueCreationDate()).isEqualTo(date2); + assertThat(result.get(2).getIssueCreationDate()).isEqualTo(date1); + } + + @Test + public void should_sort_by_update_date() { + Date date = new Date(); + Date date1 = DateUtils.addDays(date, -3); + Date date2 = DateUtils.addDays(date, -2); + Date date3 = DateUtils.addDays(date, -1); + IssueDto issue1 = new IssueDto().setId(1L).setIssueUpdateDate(date1); + IssueDto issue2 = new IssueDto().setId(2L).setIssueUpdateDate(date3); + IssueDto issue3 = new IssueDto().setId(3L).setIssueUpdateDate(date2); + List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3); + + IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.UPDATE_DATE).asc(false).build(); + IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query); + + List<IssueDto> result = newArrayList(issuesFinderSort.sort()); + + assertThat(result).hasSize(3); + assertThat(result.get(0).getIssueUpdateDate()).isEqualTo(date3); + assertThat(result.get(1).getIssueUpdateDate()).isEqualTo(date2); + assertThat(result.get(2).getIssueUpdateDate()).isEqualTo(date1); + } + + @Test + public void should_sort_by_close_date() { + Date date = new Date(); + Date date1 = DateUtils.addDays(date, -3); + Date date2 = DateUtils.addDays(date, -2); + Date date3 = DateUtils.addDays(date, -1); + IssueDto issue1 = new IssueDto().setId(1L).setIssueCloseDate(date1); + IssueDto issue2 = new IssueDto().setId(2L).setIssueCloseDate(date3); + IssueDto issue3 = new IssueDto().setId(3L).setIssueCloseDate(date2); + List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3); + + IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.CLOSE_DATE).asc(false).build(); + IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query); + + List<IssueDto> result = newArrayList(issuesFinderSort.sort()); + + assertThat(result).hasSize(3); + assertThat(result.get(0).getIssueCloseDate()).isEqualTo(date3); + assertThat(result.get(1).getIssueCloseDate()).isEqualTo(date2); + assertThat(result.get(2).getIssueCloseDate()).isEqualTo(date1); + } + +} |