From b6fe0fa6e5af97a38224c53449c316dd1978d848 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 22 Nov 2012 17:19:38 +0100 Subject: [PATCH] SONAR-3825 add more form attributes --- .../org/sonar/core/measure/MeasureFilter.java | 106 +++++------------- .../core/measure/MeasureFilterCondition.java | 7 ++ .../core/measure/MeasureFilterEngine.java | 13 ++- .../core/measure/MeasureFilterFactory.java | 101 +++++++++++++++++ .../sonar/core/measure/MeasureFilterSql.java | 10 +- .../core/measure/MeasureFilterEngineTest.java | 4 +- .../measure/MeasureFilterExecutorTest.java | 59 +++++----- .../sonar/core/measure/MeasureFilterTest.java | 38 +++++++ .../org/sonar/server/platform/Platform.java | 2 + .../java/org/sonar/server/ui/JRubyFacade.java | 2 +- .../app/controllers/favourites_controller.rb | 4 +- .../WEB-INF/app/helpers/measures_helper.rb | 38 +++++++ .../WEB-INF/app/models/measure_filter.rb | 33 ++---- .../app/views/measures/_display_list.html.erb | 22 ++-- .../app/views/measures/search.html.erb | 99 ++++++++++++---- 15 files changed, 366 insertions(+), 172 deletions(-) create mode 100644 sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java create mode 100644 sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterTest.java create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java index 42a4f3e0f0a..be5b8966ad5 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilter.java @@ -19,27 +19,27 @@ */ package org.sonar.core.measure; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import com.google.common.collect.Lists; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.measures.Metric; -import org.sonar.api.utils.DateUtils; import javax.annotation.Nullable; -import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.List; -import java.util.Map; public class MeasureFilter { - private static final String[] EMPTY = {}; // conditions on resources private String baseResourceKey; private boolean onBaseResourceChildren = false; // only if getBaseResourceKey is set - private String[] resourceScopes = EMPTY; - private String[] resourceQualifiers = EMPTY; - private String[] resourceLanguages = EMPTY; + private List resourceScopes = Collections.emptyList(); + private List resourceQualifiers = Collections.emptyList(); + private List resourceLanguages = Collections.emptyList(); private String resourceKeyRegexp; private String resourceName; private Date fromDate = null, toDate = null; @@ -69,33 +69,18 @@ public class MeasureFilter { return onBaseResourceChildren; } - public MeasureFilter setResourceScopes(String... l) { - this.resourceScopes = (l != null ? l : EMPTY); + public MeasureFilter setResourceScopes(@Nullable List list) { + this.resourceScopes = sanitize(list); return this; } - public MeasureFilter setResourceQualifiers(String... l) { - this.resourceQualifiers = l; + public MeasureFilter setResourceQualifiers(@Nullable List list) { + this.resourceQualifiers = sanitize(list); return this; } - public MeasureFilter setResourceLanguages(String... l) { - this.resourceLanguages = (l != null ? l : EMPTY); - return this; - } - - public MeasureFilter setResourceScopes(@Nullable List l) { - this.resourceScopes = (l != null ? l.toArray(new String[l.size()]) : EMPTY); - return this; - } - - public MeasureFilter setResourceQualifiers(@Nullable List l) { - this.resourceQualifiers = (l != null ? l.toArray(new String[l.size()]) : EMPTY); - return this; - } - - public MeasureFilter setResourceLanguages(@Nullable List l) { - this.resourceLanguages = (l != null ? l.toArray(new String[l.size()]) : EMPTY); + public MeasureFilter setResourceLanguages(@Nullable List list) { + this.resourceLanguages = sanitize(list); return this; } @@ -170,15 +155,15 @@ public class MeasureFilter { return toDate; } - public String[] getResourceScopes() { + public List getResourceScopes() { return resourceScopes; } - public String[] getResourceQualifiers() { + public List getResourceQualifiers() { return resourceQualifiers; } - public String[] getResourceLanguages() { + public List getResourceLanguages() { return resourceLanguages; } @@ -190,56 +175,21 @@ public class MeasureFilter { return sort; } - public static MeasureFilter create(Map properties) { - MeasureFilter filter = new MeasureFilter(); - filter.setBaseResourceKey(properties.get("base")); - filter.setResourceScopes(toArray(properties.get("scopes"))); - filter.setResourceQualifiers(toArray(properties.get("qualifiers"))); - filter.setResourceLanguages(toArray(properties.get("languages"))); - if (properties.containsKey("onBaseChildren")) { - filter.setOnBaseResourceChildren(Boolean.valueOf(properties.get("onBaseChildren"))); - } - filter.setResourceName(properties.get("nameRegexp")); - filter.setResourceKeyRegexp(properties.get("keyRegexp")); - if (properties.containsKey("fromDate")) { - filter.setFromDate(toDate(properties.get("fromDate"))); - } else if (properties.containsKey("afterDays")) { - filter.setFromDate(toDays(properties.get("afterDays"))); - } - if (properties.containsKey("toDate")) { - filter.setToDate(toDate(properties.get("toDate"))); - } else if (properties.containsKey("beforeDays")) { - filter.setToDate(toDays(properties.get("beforeDays"))); - } - - if (properties.containsKey("favourites")) { - filter.setUserFavourites(Boolean.valueOf(properties.get("favourites"))); - } - return filter; + @VisibleForTesting + static List sanitize(@Nullable List list) { + return isBlank(list) ? Collections.emptyList() : list; } - private static String[] toArray(@Nullable String s) { - if (s == null) { - return EMPTY; + private static boolean isBlank(@Nullable List list) { + boolean blank = false; + if (list == null || list.isEmpty() || (list.size() == 1 && Strings.isNullOrEmpty(list.get(0)))) { + blank = true; } - return StringUtils.split(s, ","); + return blank; } - private static Date toDate(@Nullable String date) { - if (date != null) { - return DateUtils.parseDate(date); - } - return null; + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.SIMPLE_STYLE); } - - private static Date toDays(@Nullable String s) { - if (s != null) { - int days = Integer.valueOf(s); - Date date = org.apache.commons.lang.time.DateUtils.truncate(new Date(), Calendar.DATE); - date = org.apache.commons.lang.time.DateUtils.addDays(date, -days); - return date; - } - return null; - } - } diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java index b97ec5f6143..f991b2f270f 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java @@ -19,6 +19,8 @@ */ package org.sonar.core.measure; +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.measures.Metric; public class MeasureFilterCondition { @@ -67,4 +69,9 @@ public class MeasureFilterCondition { sql.append(metric.getId()); sql.append(" AND ").append(valueColumn()).append(operator).append(value); } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.SIMPLE_STYLE); + } } diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java index fcebe106ea9..80c3627e881 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java @@ -20,6 +20,9 @@ package org.sonar.core.measure; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.collect.Maps; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.SystemUtils; import org.json.simple.parser.ParseException; import org.slf4j.Logger; @@ -35,25 +38,27 @@ public class MeasureFilterEngine implements ServerComponent { private static final Logger FILTER_LOG = LoggerFactory.getLogger("org.sonar.MEASURE_FILTER"); private final MeasureFilterDecoder decoder; + private final MeasureFilterFactory factory; private final MeasureFilterExecutor executor; - public MeasureFilterEngine(MeasureFilterDecoder decoder, MeasureFilterExecutor executor) { + public MeasureFilterEngine(MeasureFilterDecoder decoder, MeasureFilterFactory factory, MeasureFilterExecutor executor) { this.decoder = decoder; this.executor = executor; + this.factory = factory; } public List execute(String filterJson, @Nullable Long userId) throws ParseException { return execute(filterJson, userId, FILTER_LOG); } - public List execute2(Map filterMap, @Nullable Long userId) throws ParseException { + public List execute2(Map filterMap, @Nullable Long userId) throws ParseException { Logger logger = FILTER_LOG; MeasureFilterContext context = new MeasureFilterContext(); - context.setJson(filterMap.toString()); + context.setJson(Joiner.on("|").withKeyValueSeparator("=").join(filterMap)); context.setUserId(userId); try { long start = System.currentTimeMillis(); - MeasureFilter filter = MeasureFilter.create(filterMap); + MeasureFilter filter = factory.create(filterMap); List rows = executor.execute(filter, context); log(context, rows, (System.currentTimeMillis() - start), logger); return rows; diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java new file mode 100644 index 00000000000..8908226fc30 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterFactory.java @@ -0,0 +1,101 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.core.measure; + +import org.apache.commons.lang.StringUtils; +import org.sonar.api.ServerComponent; +import org.sonar.api.measures.MetricFinder; +import org.sonar.api.utils.DateUtils; + +import javax.annotation.Nullable; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; + +public class MeasureFilterFactory implements ServerComponent { + + private MetricFinder metricFinder; + + public MeasureFilterFactory(MetricFinder metricFinder) { + this.metricFinder = metricFinder; + } + + public MeasureFilter create(Map properties) { + MeasureFilter filter = new MeasureFilter(); + filter.setBaseResourceKey((String)properties.get("base")); + filter.setResourceScopes((List)properties.get("scopes")); + filter.setResourceQualifiers((List)(properties.get("qualifiers"))); + filter.setResourceLanguages((List)(properties.get("languages"))); + if (properties.containsKey("onBaseChildren")) { + filter.setOnBaseResourceChildren(Boolean.valueOf((String)properties.get("onBaseChildren"))); + } + filter.setResourceName((String)properties.get("nameRegexp")); + filter.setResourceKeyRegexp((String)properties.get("keyRegexp")); + if (properties.containsKey("fromDate")) { + filter.setFromDate(toDate((String)properties.get("fromDate"))); + } else if (properties.containsKey("afterDays")) { + filter.setFromDate(toDays((String)properties.get("afterDays"))); + } + if (properties.containsKey("toDate")) { + filter.setToDate(toDate((String)properties.get("toDate"))); + } else if (properties.containsKey("beforeDays")) { + filter.setToDate(toDays((String)properties.get("beforeDays"))); + } + + if (properties.containsKey("favourites")) { + filter.setUserFavourites(Boolean.valueOf((String)properties.get("favourites"))); + } + if (properties.containsKey("asc")) { + filter.setSortAsc(Boolean.valueOf((String)properties.get("asc"))); + } + String s = (String)properties.get("sort"); + if (s != null) { + if (StringUtils.startsWith(s, "metric:")) { + filter.setSortOnMetric(metricFinder.findByKey(StringUtils.substringAfter(s, "metric:"))); + } else { + filter.setSortOn(MeasureFilterSort.Field.valueOf(s.toUpperCase())); + } + } +// if (map.containsKey("sortPeriod")) { +// filter.setSortOnPeriod(((Long) map.get("sortPeriod")).intValue()); +// } + return filter; + } + + private static Date toDate(@Nullable String date) { + if (date != null) { + return DateUtils.parseDate(date); + } + return null; + } + + private static Date toDays(@Nullable String s) { + if (s != null) { + int days = Integer.valueOf(s); + Date date = org.apache.commons.lang.time.DateUtils.truncate(new Date(), Calendar.DATE); + date = org.apache.commons.lang.time.DateUtils.addDays(date, -days); + return date; + } + return null; + } + +} diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSql.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSql.java index bb41a9c77ed..e1ec30a4291 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSql.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSql.java @@ -149,15 +149,15 @@ class MeasureFilterSql { if (context.getBaseSnapshot() == null) { sql.append(" AND p.copy_resource_id IS NULL "); } - if (filter.getResourceQualifiers().length > 0) { + if (!filter.getResourceQualifiers().isEmpty()) { sql.append(" AND s.qualifier IN "); appendInStatement(filter.getResourceQualifiers(), sql); } - if (filter.getResourceScopes().length > 0) { + if (!filter.getResourceScopes().isEmpty()) { sql.append(" AND s.scope IN "); appendInStatement(filter.getResourceScopes(), sql); } - if (filter.getResourceLanguages().length > 0) { + if (!filter.getResourceLanguages().isEmpty()) { sql.append(" AND p.language IN "); appendInStatement(filter.getResourceLanguages(), sql); } @@ -216,7 +216,7 @@ class MeasureFilterSql { sql.append(" AND s.project_id IN (SELECT rindex.resource_id FROM resource_index rindex WHERE rindex.kee like '"); sql.append(StringEscapeUtils.escapeSql(StringUtils.lowerCase(filter.getResourceName()))); sql.append("%'"); - if (filter.getResourceQualifiers().length > 0) { + if (!filter.getResourceQualifiers().isEmpty()) { sql.append(" AND rindex.qualifier IN "); appendInStatement(filter.getResourceQualifiers(), sql); } @@ -258,7 +258,7 @@ class MeasureFilterSql { } - private static void appendInStatement(String[] values, StringBuilder to) { + private static void appendInStatement(List values, StringBuilder to) { to.append(" ('"); to.append(StringUtils.join(values, "','")); to.append("') "); diff --git a/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterEngineTest.java b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterEngineTest.java index 2b08db5c968..01a67868dce 100644 --- a/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterEngineTest.java +++ b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterEngineTest.java @@ -44,7 +44,7 @@ public class MeasureFilterEngineTest { Logger logger = mock(Logger.class); when(logger.isDebugEnabled()).thenReturn(true); - MeasureFilterEngine engine = new MeasureFilterEngine(decoder, executor); + MeasureFilterEngine engine = new MeasureFilterEngine(decoder, null, executor); final long userId = 50L; engine.execute("{}", userId, logger); @@ -69,7 +69,7 @@ public class MeasureFilterEngineTest { when(decoder.decode("")).thenThrow(new ParseException(0)); MeasureFilterExecutor executor = mock(MeasureFilterExecutor.class); - MeasureFilterEngine engine = new MeasureFilterEngine(decoder, executor); + MeasureFilterEngine engine = new MeasureFilterEngine(decoder, null, executor); engine.execute("", 50L); } } diff --git a/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterExecutorTest.java b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterExecutorTest.java index 5d3c01d107c..d71a364c238 100644 --- a/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterExecutorTest.java +++ b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterExecutorTest.java @@ -28,6 +28,7 @@ import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.SnapshotDto; import java.sql.SQLException; +import java.util.Arrays; import java.util.List; import static org.fest.assertions.Assertions.assertThat; @@ -64,7 +65,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void filter_is_not_valid_if_missing_base_snapshot() { MeasureFilterContext context = new MeasureFilterContext(); - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setOnBaseResourceChildren(true); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setOnBaseResourceChildren(true); assertThat(MeasureFilterExecutor.isValid(filter, context)).isFalse(); context.setBaseSnapshot(new SnapshotDto().setId(123L)); @@ -88,7 +89,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void filter_is_not_valid_if_anonymous_favourites() { MeasureFilterContext context = new MeasureFilterContext(); - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setUserFavourites(true); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setUserFavourites(true); assertThat(MeasureFilterExecutor.isValid(filter, context)).isFalse(); context.setUserId(123L); @@ -97,7 +98,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void projects_without_measure_conditions() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOn(MeasureFilterSort.Field.LANGUAGE); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setSortOn(MeasureFilterSort.Field.LANGUAGE); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2); @@ -107,7 +108,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void test_default_sort() { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA"); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")); assertThat(filter.sort().isAsc()).isTrue(); assertThat(filter.sort().field()).isEqualTo(MeasureFilterSort.Field.NAME); @@ -116,7 +117,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_ascending_resource_name() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortAsc(true); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")).setSortAsc(true); List rows = executor.execute(filter, new MeasureFilterContext()); // Big -> Tiny @@ -127,7 +128,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_ascending_resource_key() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortAsc(true).setSortOn(MeasureFilterSort.Field.KEY); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")).setSortAsc(true).setSortOn(MeasureFilterSort.Field.KEY); List rows = executor.execute(filter, new MeasureFilterContext()); // Big -> Tiny @@ -138,7 +139,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_ascending_resource_version() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortAsc(true).setSortOn(MeasureFilterSort.Field.VERSION); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setSortAsc(true).setSortOn(MeasureFilterSort.Field.VERSION); List rows = executor.execute(filter, new MeasureFilterContext()); // Java Project 1.0 then Php Project 3.0 @@ -149,7 +150,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_descending_resource_name() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortAsc(false); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")).setSortAsc(false); List rows = executor.execute(filter, new MeasureFilterContext()); // Tiny -> Big @@ -160,7 +161,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_ascending_text_measure() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOnMetric(METRIC_PROFILE); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setSortOnMetric(METRIC_PROFILE); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2); @@ -170,7 +171,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_descending_text_measure() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOnMetric(METRIC_PROFILE).setSortAsc(false); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setSortOnMetric(METRIC_PROFILE).setSortAsc(false); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2); @@ -181,7 +182,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_missing_text_measure() throws SQLException { // the metric 'profile' is not set on files - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortOnMetric(METRIC_PROFILE); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")).setSortOnMetric(METRIC_PROFILE); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2);//2 files randomly sorted @@ -189,7 +190,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_ascending_numeric_measure() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortOnMetric(METRIC_LINES); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")).setSortOnMetric(METRIC_LINES); List rows = executor.execute(filter, new MeasureFilterContext()); // Tiny -> Big @@ -200,7 +201,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_descending_numeric_measure() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortOnMetric(METRIC_LINES).setSortAsc(false); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")).setSortOnMetric(METRIC_LINES).setSortAsc(false); List rows = executor.execute(filter, new MeasureFilterContext()); // Big -> Tiny @@ -212,7 +213,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_missing_numeric_measure() throws SQLException { // coverage measures are not computed - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortOnMetric(METRIC_COVERAGE); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")).setSortOnMetric(METRIC_COVERAGE); List rows = executor.execute(filter, new MeasureFilterContext()); // 2 files, random order @@ -221,7 +222,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_ascending_variation() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOnMetric(METRIC_LINES).setSortOnPeriod(5); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setSortOnMetric(METRIC_LINES).setSortOnPeriod(5); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2); @@ -231,7 +232,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_descending_variation() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK") + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")) .setSortOnMetric(METRIC_LINES).setSortOnPeriod(5).setSortAsc(false); List rows = executor.execute(filter, new MeasureFilterContext()); @@ -242,7 +243,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_ascending_date() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOn(MeasureFilterSort.Field.DATE); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setSortOn(MeasureFilterSort.Field.DATE); List rows = executor.execute(filter, new MeasureFilterContext()); verifyJavaProject(rows.get(0));// 2008 @@ -251,7 +252,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void sort_by_descending_date() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOn(MeasureFilterSort.Field.DATE).setSortAsc(false); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setSortOn(MeasureFilterSort.Field.DATE).setSortAsc(false); List rows = executor.execute(filter, new MeasureFilterContext()); verifyPhpProject(rows.get(0));// 2012 @@ -260,7 +261,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void condition_on_numeric_measure() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA") + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")) .setSortOnMetric(METRIC_LINES) .addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 200)); List rows = executor.execute(filter, new MeasureFilterContext()); @@ -271,7 +272,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void condition_on_measure_variation() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK") + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")) .setSortOnMetric(METRIC_LINES) .addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 1000).setPeriod(5)); List rows = executor.execute(filter, new MeasureFilterContext()); @@ -282,7 +283,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void multiple_conditions_on_numeric_measures() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA") + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")) .setSortOnMetric(METRIC_LINES) .addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 2)) .addCondition(new MeasureFilterCondition(METRIC_LINES, "<=", 50)); @@ -294,7 +295,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void filter_by_language() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setResourceLanguages("java", "cobol"); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setResourceLanguages(Arrays.asList("java", "cobol")); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); @@ -303,7 +304,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void filter_by_min_date() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setFromDate(DateUtils.parseDate("2012-12-13")); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setFromDate(DateUtils.parseDate("2012-12-13")); List rows = executor.execute(filter, new MeasureFilterContext()); // php has been analyzed in 2012-12-13, whereas java project has been analyzed in 2008 @@ -313,7 +314,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void filter_by_range_of_dates() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK") + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")) .setFromDate(DateUtils.parseDate("2007-01-01")) .setToDate(DateUtils.parseDate("2010-01-01")); List rows = executor.execute(filter, new MeasureFilterContext()); @@ -325,7 +326,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void filter_by_resource_name() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setResourceName("PHP Proj"); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setResourceName("PHP Proj"); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); @@ -334,7 +335,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void filter_by_resource_key_star_regexp() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setResourceKeyRegexp("java*"); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setResourceKeyRegexp("java*"); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); @@ -343,7 +344,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void filter_by_resource_key_exclamation_mark() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setResourceKeyRegexp("JaV?_proje*"); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK")).setResourceKeyRegexp("JaV?_proje*"); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); verifyJavaProject(rows.get(0)); @@ -351,7 +352,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void filter_by_base_resource() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setBaseResourceKey("java_project"); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("CLA")).setBaseResourceKey("java_project"); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2); @@ -371,7 +372,7 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { @Test public void filter_by_parent_without_children() throws SQLException { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK", "PAC", "CLA").setBaseResourceKey("java_project:org.sonar.foo.Big").setOnBaseResourceChildren(true); + MeasureFilter filter = new MeasureFilter().setResourceQualifiers(Arrays.asList("TRK", "PAC", "CLA")).setBaseResourceKey("java_project:org.sonar.foo.Big").setOnBaseResourceChildren(true); List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).isEmpty(); diff --git a/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterTest.java b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterTest.java new file mode 100644 index 00000000000..74f30fa727e --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterTest.java @@ -0,0 +1,38 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.core.measure; + +import com.google.common.collect.Lists; +import org.junit.Test; + +import java.util.Arrays; + +import static org.fest.assertions.Assertions.assertThat; + +public class MeasureFilterTest { + @Test + public void should_sanitize_list() { + assertThat(MeasureFilter.sanitize(null)).isEmpty(); + assertThat(MeasureFilter.sanitize(Lists.newArrayList())).isEmpty(); + assertThat(MeasureFilter.sanitize(Arrays.asList(""))).isEmpty(); + assertThat(MeasureFilter.sanitize(Lists.newArrayList("TRK"))).containsExactly("TRK"); + assertThat(MeasureFilter.sanitize(Lists.newArrayList("TRK", "BRC"))).containsExactly("TRK", "BRC"); + } +} 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 956d72cfb95..79d5345884f 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 @@ -43,6 +43,7 @@ import org.sonar.core.i18n.RuleI18nManager; import org.sonar.core.measure.MeasureFilterDecoder; import org.sonar.core.measure.MeasureFilterEngine; import org.sonar.core.measure.MeasureFilterExecutor; +import org.sonar.core.measure.MeasureFilterFactory; import org.sonar.core.metric.DefaultMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; import org.sonar.core.persistence.DaoUtils; @@ -241,6 +242,7 @@ public final class Platform { servicesContainer.addSingleton(SettingsChangeNotifier.class); servicesContainer.addSingleton(PageDecorations.class); servicesContainer.addSingleton(MeasureFilterDecoder.class); + servicesContainer.addSingleton(MeasureFilterFactory.class); servicesContainer.addSingleton(MeasureFilterExecutor.class); servicesContainer.addSingleton(MeasureFilterEngine.class); servicesContainer.addSingleton(DryRunDatabaseFactory.class); diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index cbedd251521..e362cbb6bfb 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -102,7 +102,7 @@ public final class JRubyFacade { return get(MeasureFilterEngine.class).execute(json, userId); } - public List executeMeasureFilter2(Map map, @Nullable Long userId) throws ParseException { + public List executeMeasureFilter2(Map map, @Nullable Long userId) throws ParseException { return get(MeasureFilterEngine.class).execute2(map, userId); } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/favourites_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/favourites_controller.rb index ea8ebd54e8e..a529ac6d273 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/favourites_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/favourites_controller.rb @@ -30,7 +30,7 @@ class FavouritesController < ApplicationController else current_user.add_favourite(favourite_id) @style='fav' - @tooltip='Click to delete from favourites' + @tooltip='Click to remove from favourites' end star_id=params[:elt] @@ -40,7 +40,7 @@ class FavouritesController < ApplicationController page.element.addClassName(star_id, @style) page.element.writeAttribute(star_id, 'alt', @tooltip) page.element.writeAttribute(star_id, 'title', @tooltip) - end + end end end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb new file mode 100644 index 00000000000..6d4beff64a9 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb @@ -0,0 +1,38 @@ +# +# Sonar, entreprise quality control tool. +# Copyright (C) 2008-2012 SonarSource +# mailto:contact AT sonarsource DOT com +# +# Sonar 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. +# +# Sonar 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 Sonar; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 +# +module MeasuresHelper + + def list_column_title(filter, column, url_params) + if column.sort? + html = link_to(h(column.display_name), url_params.merge({:controller => 'measures', :action => 'search', :asc => (!filter.sort_asc?).to_s, :sort => column.key})) + else + html=h(column.display_name) + end + #if column.variation + # html=" #{html}" + #end + + if filter.sort_key==column.key + html << (filter.sort_asc? ? image_tag("asc12.png") : image_tag("desc12.png")) + end + "#{html}" + end + +end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb index f8c4c8cc55d..fd71d2aa752 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/measure_filter.rb @@ -44,23 +44,17 @@ class MeasureFilter # Column to be displayed class Column - attr_reader :key + attr_reader :key, :metric def initialize(key) @key = key - end - - def metric - @metric ||= - begin - metric_key = @key.split(':')[1] - Metric.by_key(metric_key) if metric_key - end + metric_key = @key.split(':')[1] + @metric = Metric.by_key(metric_key) if metric_key end def display_name - if metric - Api::Utils.message("metric.#{metric.key}.name", :default => metric.short_name) + if @metric + Api::Utils.message("metric.#{@metric.key}.name", :default => @metric.short_name) else Api::Utils.message("filters.col.#{@key}", :default => @key) end @@ -146,7 +140,6 @@ class MeasureFilter user = options[:user] rows=Api::Utils.java_facade.executeMeasureFilter2(@criteria, (user ? user.id : nil)) snapshot_ids = filter_authorized_snapshot_ids(rows, controller) - snapshot_ids = paginate_snapshot_ids(snapshot_ids) init_data(snapshot_ids) self @@ -161,9 +154,7 @@ class MeasureFilter end def set_criteria_default_value(key, value) - if !@criteria.has_key?(key.to_sym) - set_criteria_value(key, value) - end + set_criteria_value(key, value) unless @criteria.has_key?(key.to_sym) end private @@ -193,12 +184,8 @@ class MeasureFilter authorized_project_ids = controller.select_authorized(:user, project_ids) snapshot_ids = rows.map { |row| row.getSnapshotId() if authorized_project_ids.include?(row.getResourceRootId()) }.compact @security_exclusions = (snapshot_ids.size + <% if display_favourites %><% end %> <% @filter.columns.each do |column| %> - <% if column.metric %> - - <% elsif column.key=='name' %> - - <% else %> - - <% end %> + <%= list_column_title(@filter, column, url_params) -%> <% end %> <% @filter.data.each do |data| %> + <% if display_favourites %><% end %> <% @filter.columns.each do |column| %> <% if column.metric %> <% end %> + <% if @filter.data.empty? %> + + + + <% end %> - <%= render :partial => 'utils/tfoot_pagination', :locals => {:pagination => @filter.pagination, :colspan => @filter.columns.size} %> + <%= render :partial => 'utils/tfoot_pagination', :locals => {:pagination => @filter.pagination, :colspan => colspan} %>
<%= Api::Utils.message("metric.#{column.metric.key}.name", :default => column.metric.short_name) -%><%= Api::Utils.message("filters.col.name") -%><%= Api::Utils.message("filters.col.#{column.key}", :default => column.key) -%>
<%= link_to_favourite(data.snapshot.resource) -%> @@ -36,6 +37,11 @@ <% end %>
<%= message 'no_data' -%>
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb index fd1fc74d54c..09750fc6be3 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/measures/search.html.erb @@ -5,13 +5,22 @@ width: 240px; } + #filter-form select { + width: 200px; + padding: 1px; + } + + #filter-form select option { + padding: 1px 10px 1px 4px; + } + #filter-result { padding-left: 250px; } <% end %> -<% url_options = params.reject { |k, v| v.empty? || k=='search' }.merge({:controller => 'measures', :action => 'search'}) %> +<% url_params = params.reject { |k, v| v.nil? || v=='' || k.starts_with?('_') }.merge({:controller => 'measures', :action => 'search'}) %>
@@ -19,37 +28,78 @@ - - + + + + - + + + - - + - - + - - + - - + + + + + + + + + + + + +
Base: + Base:
+ +
+ Direct children:
+ <%= check_box_tag 'onBaseChildren', 'true', params['onBaseChildren']=='true' -%> +
Qualifiers: -
+ Language:
+
Language: + Name:
+
Name: + Key:
+
Key: + Favourites only:
+ <%= check_box_tag 'favourites', 'true', params['favourites']=='true' %> +
+ From date:
+ +
+ To date:
+ +
+ Before:
+ days +
After:
+ days +
+ + Reset +
@@ -57,15 +107,20 @@
<% if @filter %>
+ Display as: <% MeasureFilter::DISPLAYS.each do |display| %> - <%= link_to display.key, url_options.merge(:display => display.key) -%> + <%= link_to_if display.key!=@filter.display.key, display.key, url_params.merge(:display => display.key) -%> <% end %> - <%= render :partial => "measures/display_#{@filter.display.key}.html.erb" -%> + <%= render :partial => "measures/display_#{@filter.display.key}", :locals => {:url_params => url_params} -%>

- <% permalink = url_for(url_options) %> + <% permalink = url_for(url_params) %> Permalink:

+ <% if @filter.security_exclusions %> +

<%= message('results_not_display_due_to_security') -%>

+ <% end %> +
<% end %> -- 2.39.5