From 223d3f26c6391622c0770f3bc65fdbd54b85233c Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Tue, 9 Oct 2012 13:49:01 +0200 Subject: [PATCH] SONAR-3621 use JSON format between rails and java components This is the first step before the refactoring of the tables FILTERS, FILTER_COLUMNS and CRITERIA --- .../src/main/assembly/conf/logback.xml | 4 + sonar-core/pom.xml | 9 +- .../org/sonar/core/measure/MeasureFilter.java | 57 ++- ...ition.java => MeasureFilterCondition.java} | 38 +- .../core/measure/MeasureFilterContext.java | 57 +-- .../core/measure/MeasureFilterDecoder.java | 132 ++++++ .../core/measure/MeasureFilterEngine.java | 74 ++++ .../core/measure/MeasureFilterExecutor.java | 46 +- .../sonar/core/measure/MeasureFilterSort.java | 12 +- .../sonar/core/measure/MeasureFilterSql.java | 62 +-- .../measure/MeasureFilterContextTest.java} | 30 +- .../measure/MeasureFilterDecoderTest.java | 121 ++++++ .../core/measure/MeasureFilterEngineTest.java | 55 +++ .../measure/MeasureFilterExecutorTest.java | 133 +++--- sonar-server/src/dev/h2/conf/logback.xml | 4 + sonar-server/src/dev/mysql/conf/logback.xml | 4 + .../src/dev/postgresql/conf/logback.xml | 4 + .../java/org/sonar/server/filters/Filter.java | 393 ------------------ .../sonar/server/filters/FilterExecutor.java | 250 ----------- .../sonar/server/filters/FilterResult.java | 142 ------- .../server/filters/MeasureCriterion.java | 76 ---- .../org/sonar/server/platform/Platform.java | 28 +- .../java/org/sonar/server/ui/JRubyFacade.java | 38 +- .../main/webapp/WEB-INF/app/models/filter.rb | 2 +- .../WEB-INF/app/models/filter_context.rb | 20 +- .../main/webapp/WEB-INF/app/models/filters.rb | 99 ++--- .../server/filters/DateCriterionTest.java | 47 --- .../server/filters/FilterExecutorTest.java | 361 ---------------- .../server/filters/FilterResultTest.java | 46 -- .../filters/FilterExecutorTest/measures.xml | 68 --- .../filters/FilterExecutorTest/shared.xml | 39 -- 31 files changed, 700 insertions(+), 1751 deletions(-) rename sonar-core/src/main/java/org/sonar/core/measure/{MeasureFilterValueCondition.java => MeasureFilterCondition.java} (63%) rename sonar-server/src/main/java/org/sonar/server/filters/DateCriterion.java => sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterContext.java (58%) create mode 100644 sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterDecoder.java create mode 100644 sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java rename sonar-core/src/{main/java/org/sonar/core/measure/MesasureFilterContext.java => test/java/org/sonar/core/measure/MeasureFilterContextTest.java} (61%) create mode 100644 sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterDecoderTest.java create mode 100644 sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterEngineTest.java delete mode 100644 sonar-server/src/main/java/org/sonar/server/filters/Filter.java delete mode 100644 sonar-server/src/main/java/org/sonar/server/filters/FilterExecutor.java delete mode 100644 sonar-server/src/main/java/org/sonar/server/filters/FilterResult.java delete mode 100644 sonar-server/src/main/java/org/sonar/server/filters/MeasureCriterion.java delete mode 100644 sonar-server/src/test/java/org/sonar/server/filters/DateCriterionTest.java delete mode 100644 sonar-server/src/test/java/org/sonar/server/filters/FilterExecutorTest.java delete mode 100644 sonar-server/src/test/java/org/sonar/server/filters/FilterResultTest.java delete mode 100644 sonar-server/src/test/resources/org/sonar/server/filters/FilterExecutorTest/measures.xml delete mode 100644 sonar-server/src/test/resources/org/sonar/server/filters/FilterExecutorTest/shared.xml diff --git a/sonar-application/src/main/assembly/conf/logback.xml b/sonar-application/src/main/assembly/conf/logback.xml index 1c36ddc015a..63913b48eea 100644 --- a/sonar-application/src/main/assembly/conf/logback.xml +++ b/sonar-application/src/main/assembly/conf/logback.xml @@ -81,6 +81,10 @@ + + + + diff --git a/sonar-core/pom.xml b/sonar-core/pom.xml index b54ae40570b..5dc59272289 100644 --- a/sonar-core/pom.xml +++ b/sonar-core/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -64,7 +65,11 @@ org.codehaus.plexus plexus-classworlds - + + com.googlecode.json-simple + json-simple + 1.1.1 + 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 d3a9ffd0b36..0fbf9f2f6d6 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 @@ -20,31 +20,31 @@ package org.sonar.core.measure; import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import org.sonar.api.measures.Metric; +import javax.annotation.Nullable; +import java.util.Collections; import java.util.Date; import java.util.List; -import java.util.Set; public class MeasureFilter { // conditions on resources private String baseResourceKey; - private boolean onBaseResourceChildren = false; // only if baseResourceKey is set - private Set resourceScopes = Sets.newHashSet(); - private Set resourceQualifiers = Sets.newHashSet(); - private Set resourceLanguages = Sets.newHashSet(); + private boolean onBaseResourceChildren = false; // only if getBaseResourceKey is set + private List resourceScopes = Lists.newArrayList(); + private List resourceQualifiers = Lists.newArrayList(); + private List resourceLanguages = Lists.newArrayList(); private String resourceName; private Date fromDate = null, toDate = null; private boolean userFavourites = false; // conditions on measures - private List measureConditions = Lists.newArrayList(); + private List measureConditions = Lists.newArrayList(); // sort private MeasureFilterSort sort = new MeasureFilterSort(); - public String baseResourceKey() { + public String getBaseResourceKey() { return baseResourceKey; } @@ -62,18 +62,28 @@ public class MeasureFilter { return onBaseResourceChildren; } - public MeasureFilter setResourceScopes(Set resourceScopes) { - this.resourceScopes = resourceScopes; + public MeasureFilter setResourceScopes(@Nullable List l) { + this.resourceScopes = (l != null ? l : Collections.emptyList()); return this; } - public MeasureFilter setResourceQualifiers(String... qualifiers) { - this.resourceQualifiers = Sets.newHashSet(qualifiers); + public MeasureFilter setResourceQualifiers(List l) { + this.resourceQualifiers = (l != null ? l : Collections.emptyList()); return this; } - public MeasureFilter setResourceLanguages(String... languages) { - this.resourceLanguages = Sets.newHashSet(languages); + public MeasureFilter setResourceQualifiers(String... l) { + this.resourceQualifiers = Lists.newArrayList(l); + return this; + } + + public MeasureFilter setResourceLanguages(List l) { + this.resourceLanguages = (l != null ? l : Collections.emptyList()); + return this; + } + + public MeasureFilter setResourceLanguages(String... l) { + this.resourceLanguages = Lists.newArrayList(l); return this; } @@ -82,11 +92,11 @@ public class MeasureFilter { return this; } - public boolean userFavourites() { + public boolean isOnFavourites() { return userFavourites; } - public String resourceName() { + public String getResourceName() { return resourceName; } @@ -95,7 +105,7 @@ public class MeasureFilter { return this; } - public MeasureFilter addCondition(MeasureFilterValueCondition condition) { + public MeasureFilter addCondition(MeasureFilterCondition condition) { this.measureConditions.add(condition); return this; } @@ -111,6 +121,7 @@ public class MeasureFilter { } public MeasureFilter setSortOnMetric(Metric m) { + this.sort.setField(MeasureFilterSort.Field.METRIC); this.sort.setMetric(m); return this; } @@ -130,27 +141,27 @@ public class MeasureFilter { return this; } - public Date fromDate() { + public Date getFromDate() { return fromDate; } - public Date toDate() { + public Date getToDate() { return toDate; } - public Set resourceScopes() { + public List getResourceScopes() { return resourceScopes; } - public Set resourceQualifiers() { + public List getResourceQualifiers() { return resourceQualifiers; } - public Set resourceLanguages() { + public List getResourceLanguages() { return resourceLanguages; } - public List measureConditions() { + public List getMeasureConditions() { return measureConditions; } diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterValueCondition.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java similarity index 63% rename from sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterValueCondition.java rename to sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java index 145e7d7bd86..b97ec5f6143 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterValueCondition.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterCondition.java @@ -21,35 +21,21 @@ package org.sonar.core.measure; import org.sonar.api.measures.Metric; -public class MeasureFilterValueCondition { - - public static enum Operator { - GREATER(">"), GREATER_OR_EQUALS(">="), EQUALS("="), LESS("<"), LESS_OR_EQUALS("<="); - - private String sql; - - private Operator(String sql) { - this.sql = sql; - } - - public String getSql() { - return sql; - } - } +public class MeasureFilterCondition { private final Metric metric; - private final Operator operator; - private final float value; - private int period = -1; + private final String operator; + private final double value; + private Integer period = null; - public MeasureFilterValueCondition(Metric metric, Operator operator, float value) { + public MeasureFilterCondition(Metric metric, String operator, double value) { this.metric = metric; this.operator = operator; this.value = value; } - public MeasureFilterValueCondition setPeriod(int period) { - this.period = (period > 0 ? period : -1); + public MeasureFilterCondition setPeriod(Integer period) { + this.period = period; return this; } @@ -57,7 +43,7 @@ public class MeasureFilterValueCondition { return metric; } - public Operator operator() { + public String operator() { return operator; } @@ -65,20 +51,20 @@ public class MeasureFilterValueCondition { return value; } - public int period() { + public Integer period() { return period; } String valueColumn() { - if (period > 0) { + if (period != null) { return "pm.variation_value_" + period; } return "pm.value"; } - void appendSql(StringBuilder sql) { + void appendSqlCondition(StringBuilder sql) { sql.append(" pm.metric_id="); sql.append(metric.getId()); - sql.append(" AND ").append(valueColumn()).append(operator.getSql()).append(value); + sql.append(" AND ").append(valueColumn()).append(operator).append(value); } } diff --git a/sonar-server/src/main/java/org/sonar/server/filters/DateCriterion.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterContext.java similarity index 58% rename from sonar-server/src/main/java/org/sonar/server/filters/DateCriterion.java rename to sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterContext.java index b1c290386be..5c0ad12b691 100644 --- a/sonar-server/src/main/java/org/sonar/server/filters/DateCriterion.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterContext.java @@ -17,57 +17,60 @@ * License along with Sonar; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ -package org.sonar.server.filters; +package org.sonar.core.measure; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; -import org.apache.commons.lang.time.DateUtils; +import org.sonar.core.resource.SnapshotDto; -import java.util.Calendar; -import java.util.Date; +class MeasureFilterContext { + private Long userId; + private SnapshotDto baseSnapshot; + private String sql; + private String json; -public class DateCriterion { - - private String operator; - private Date date; - - public DateCriterion(String operator, Date date) { - this.operator = operator; - this.date = date; + Long getUserId() { + return userId; } - public DateCriterion() { + MeasureFilterContext setUserId(Long userId) { + this.userId = userId; + return this; } - public String getOperator() { - return operator; + SnapshotDto getBaseSnapshot() { + return baseSnapshot; } - public DateCriterion setOperator(String operator) { - this.operator = operator; + MeasureFilterContext setBaseSnapshot(SnapshotDto baseSnapshot) { + this.baseSnapshot = baseSnapshot; return this; } - public Date getDate() { - return date; + String getSql() { + return sql; } - public DateCriterion setDate(Date date) { - this.date = date; + MeasureFilterContext setSql(String sql) { + this.sql = sql; return this; } - public DateCriterion setDate(int daysAgo) { - this.date = DateUtils.addDays(new Date(), -daysAgo); - this.date = DateUtils.truncate(this.date, Calendar.DATE); + String getJson() { + return json; + } + + MeasureFilterContext setJson(String json) { + this.json = json; return this; } @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) - .append("operator", operator) - .append("date", date) - .toString(); + .append("json", json) + .append("sql", sql) + .append("user", userId) + .toString(); } } diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterDecoder.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterDecoder.java new file mode 100644 index 00000000000..7cf092bed00 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterDecoder.java @@ -0,0 +1,132 @@ +/* + * 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.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.sonar.api.ServerComponent; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.MetricFinder; +import org.sonar.api.utils.DateUtils; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +public class MeasureFilterDecoder implements ServerComponent { + private MetricFinder metricFinder; + private JSONParser parser = new JSONParser(); + + public MeasureFilterDecoder(MetricFinder metricFinder) { + this.metricFinder = metricFinder; + } + + public MeasureFilter decode(String text) throws ParseException { + MeasureFilter filter = new MeasureFilter(); + JSONObject map = (JSONObject) parser.parse(text); + parseResourceConditions(filter, map); + parseMeasureConditions(map, filter); + parseSorting(map, filter); + return filter; + } + + private void parseResourceConditions(MeasureFilter filter, JSONObject map) { + filter.setBaseResourceKey((String) map.get("base")); + if (map.containsKey("onBaseChildren")) { + filter.setOnBaseResourceChildren(((Boolean) map.get("onBaseChildren")).booleanValue()); + } + filter.setResourceScopes((List) map.get("scopes")); + filter.setResourceQualifiers((List) map.get("qualifiers")); + filter.setResourceLanguages((List) map.get("languages")); + filter.setResourceName((String) map.get("name")); + + if (map.containsKey("fromDate")) { + filter.setFromDate(toDate(map, "fromDate")); + } + if (map.containsKey("afterDays")) { + filter.setFromDate(toDays(map, "afterDays")); + } + if (map.containsKey("toDate")) { + filter.setToDate(toDate(map, "toDate")); + } + if (map.containsKey("beforeDays")) { + filter.setToDate(toDays(map, "beforeDays")); + } + if (map.containsKey("favourites")) { + filter.setUserFavourites(((Boolean) map.get("favourites")).booleanValue()); + } + } + + private void parseSorting(JSONObject map, MeasureFilter filter) { + if (map.containsKey("sortAsc")) { + filter.setSortAsc(((Boolean) map.get("sortAsc")).booleanValue()); + } + String s = (String) map.get("sortField"); + if (s != null) { + filter.setSortOn(MeasureFilterSort.Field.valueOf(s)); + } + s = (String) map.get("sortField"); + if (s != null) { + filter.setSortOn(MeasureFilterSort.Field.valueOf((String) map.get("sortField"))); + } + s = (String) map.get("sortMetric"); + if (s != null) { + filter.setSortOnMetric(metricFinder.findByKey(s)); + } + if (map.containsKey("sortPeriod")) { + filter.setSortOnPeriod(((Long) map.get("sortPeriod")).intValue()); + } + } + + private void parseMeasureConditions(JSONObject map, MeasureFilter filter) { + JSONArray conditions = (JSONArray) map.get("conditions"); + if (conditions != null) { + for (Object obj : conditions) { + JSONObject c = (JSONObject) obj; + Metric metric = metricFinder.findByKey((String) c.get("metric")); + String operator = (String) c.get("op"); + Double value = (Double) c.get("val"); + MeasureFilterCondition condition = new MeasureFilterCondition(metric, operator, value); + if (c.containsKey("period")) { + condition.setPeriod(((Long) c.get("period")).intValue()); + } + filter.addCondition(condition); + } + } + } + + private static Date toDate(JSONObject map, String key) { + String date = (String) map.get(key); + if (date != null) { + return DateUtils.parseDate(date); + } + return null; + } + + private static Date toDays(JSONObject map, String key) { + int days = ((Long) map.get(key)).intValue(); + 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; + } + +} 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 new file mode 100644 index 00000000000..2b5da9a1420 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterEngine.java @@ -0,0 +1,74 @@ +/* + * 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.annotations.VisibleForTesting; +import org.apache.commons.lang.SystemUtils; +import org.json.simple.parser.ParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.ServerComponent; + +import javax.annotation.Nullable; +import java.util.List; + +public class MeasureFilterEngine implements ServerComponent { + private static final Logger FILTER_LOG = LoggerFactory.getLogger("org.sonar.MEASURE_FILTER"); + + private final MeasureFilterDecoder decoder; + private final MeasureFilterExecutor executor; + + public MeasureFilterEngine(MeasureFilterDecoder decoder, MeasureFilterExecutor executor) { + this.decoder = decoder; + this.executor = executor; + } + + public List execute(String filterJson, @Nullable Long userId) throws ParseException { + return execute(filterJson, userId, FILTER_LOG); + } + + @VisibleForTesting + List execute(String filterJson, @Nullable Long userId, Logger logger) { + MeasureFilterContext context = new MeasureFilterContext(); + context.setJson(filterJson); + context.setUserId(userId); + try { + long start = System.currentTimeMillis(); + MeasureFilter filter = decoder.decode(filterJson); + List rows = executor.execute(filter, context); + log(context, rows, (System.currentTimeMillis() - start), logger); + return rows; + } catch (Exception e) { + throw new IllegalStateException("Fail to execute filter: " + context, e); + } + } + + private void log(MeasureFilterContext context, List rows, long durationMs, Logger logger) { + if (logger.isDebugEnabled()) { + StringBuilder log = new StringBuilder(); + log.append(SystemUtils.LINE_SEPARATOR); + log.append(" json: ").append(context.getJson()).append(SystemUtils.LINE_SEPARATOR); + log.append(" sql: ").append(context.getSql()).append(SystemUtils.LINE_SEPARATOR); + log.append("results: ").append(rows.size()).append(" rows in ").append(durationMs).append("ms").append(SystemUtils.LINE_SEPARATOR); + logger.debug(log.toString()); + } + } + +} diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterExecutor.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterExecutor.java index eb66a9aa0b4..b529f5a384a 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterExecutor.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterExecutor.java @@ -20,20 +20,17 @@ package org.sonar.core.measure; import org.apache.ibatis.session.SqlSession; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.sonar.api.ServerComponent; import org.sonar.core.persistence.Database; import org.sonar.core.persistence.MyBatis; import org.sonar.core.resource.ResourceDao; -import javax.annotation.Nullable; import java.sql.Connection; +import java.sql.SQLException; import java.util.Collections; import java.util.List; public class MeasureFilterExecutor implements ServerComponent { - private static final Logger FILTER_LOG = LoggerFactory.getLogger("org.sonar.MEASURE_FILTER"); private MyBatis mybatis; private Database database; @@ -45,24 +42,21 @@ public class MeasureFilterExecutor implements ServerComponent { this.resourceDao = resourceDao; } - public List execute(MeasureFilter filter, @Nullable Long userId) { + public List execute(MeasureFilter filter, MeasureFilterContext context) throws SQLException { List rows; SqlSession session = null; try { session = mybatis.openSession(); - MesasureFilterContext context = prepareContext(filter, userId, session); + prepareContext(context, filter, session); if (isValid(filter, context)) { MeasureFilterSql sql = new MeasureFilterSql(database, filter, context); + context.setSql(sql.sql()); Connection connection = session.getConnection(); rows = sql.execute(connection); } else { rows = Collections.emptyList(); } - - } catch (Exception e) { - throw new IllegalStateException(e); - } finally { MyBatis.closeQuietly(session); } @@ -70,19 +64,31 @@ public class MeasureFilterExecutor implements ServerComponent { return rows; } - private MesasureFilterContext prepareContext(MeasureFilter filter, Long userId, SqlSession session) { - MesasureFilterContext context = new MesasureFilterContext(); - context.setUserId(userId); - if (filter.baseResourceKey() != null) { - context.setBaseSnapshot(resourceDao.getLastSnapshot(filter.baseResourceKey(), session)); + + private void prepareContext(MeasureFilterContext context, MeasureFilter filter, SqlSession session) { + if (filter.getBaseResourceKey() != null) { + context.setBaseSnapshot(resourceDao.getLastSnapshot(filter.getBaseResourceKey(), session)); } - return context; } - static boolean isValid(MeasureFilter filter, MesasureFilterContext context) { - return - !(filter.resourceQualifiers().isEmpty() && !filter.userFavourites()) && + static boolean isValid(MeasureFilter filter, MeasureFilterContext context) { + boolean valid = !(filter.isOnBaseResourceChildren() && context.getBaseSnapshot() == null) && - !(filter.userFavourites() && context.getUserId() == null); + !(filter.isOnFavourites() && context.getUserId() == null); + for (MeasureFilterCondition condition : filter.getMeasureConditions()) { + if (condition.period() != null && condition.period() < 1) { + valid = false; + } + if (condition.metric() == null) { + valid = false; + } + } + if (filter.sort().getPeriod() != null && filter.sort().getPeriod() < 1) { + valid = false; + } + if (filter.sort().onMeasures() && filter.sort().metric() == null) { + valid = false; + } + return valid; } } diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSort.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSort.java index 3abb3672664..d0e2d53f731 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSort.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasureFilterSort.java @@ -28,7 +28,7 @@ class MeasureFilterSort { private Field field = Field.NAME; private Metric metric = null; - private int period = -1; + private Integer period = null; private boolean asc = true; MeasureFilterSort() { @@ -43,8 +43,12 @@ class MeasureFilterSort { this.metric = metric; } - void setPeriod(int period) { - this.period = (period > 0 ? period : -1); + Integer getPeriod() { + return period; + } + + void setPeriod(Integer period) { + this.period = period; } void setAsc(boolean asc) { @@ -92,7 +96,7 @@ class MeasureFilterSort { break; case METRIC: if (metric.isNumericType()) { - column = (period > 0 ? "pm.variation_value_" + period : "pm.value"); + column = (period != null ? "pm.variation_value_" + period : "pm.value"); } else { column = "pm.text_value"; } 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 e59708c8467..3952a86396f 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 @@ -39,11 +39,11 @@ class MeasureFilterSql { private final Database database; private final MeasureFilter filter; - private final MesasureFilterContext context; + private final MeasureFilterContext context; private final StringBuilder sql = new StringBuilder(1000); private final List dateParameters = Lists.newArrayList(); - MeasureFilterSql(Database database, MeasureFilter filter, MesasureFilterContext context) { + MeasureFilterSql(Database database, MeasureFilter filter, MeasureFilterContext context) { this.database = database; this.filter = filter; this.context = context; @@ -71,22 +71,22 @@ class MeasureFilterSql { private void init() { sql.append("SELECT block.id, max(block.rid) rid, max(block.rootid) rootid, max(sortval) sortval"); - for (int index = 0; index < filter.measureConditions().size(); index++) { + for (int index = 0; index < filter.getMeasureConditions().size(); index++) { sql.append(", max(crit_").append(index).append(")"); } sql.append(" FROM ("); appendSortBlock(); - for (int index = 0; index < filter.measureConditions().size(); index++) { - MeasureFilterValueCondition condition = filter.measureConditions().get(index); + for (int index = 0; index < filter.getMeasureConditions().size(); index++) { + MeasureFilterCondition condition = filter.getMeasureConditions().get(index); sql.append(" UNION "); appendConditionBlock(index, condition); } sql.append(") block GROUP BY block.id"); - if (!filter.measureConditions().isEmpty()) { + if (!filter.getMeasureConditions().isEmpty()) { sql.append(" HAVING "); - for (int index = 0; index < filter.measureConditions().size(); index++) { + for (int index = 0; index < filter.getMeasureConditions().size(); index++) { if (index > 0) { sql.append(" AND "); } @@ -101,8 +101,8 @@ class MeasureFilterSql { private void appendSortBlock() { sql.append(" SELECT s.id, s.project_id rid, s.root_project_id rootid, ").append(filter.sort().column()).append(" sortval"); - for (int index = 0; index < filter.measureConditions().size(); index++) { - MeasureFilterValueCondition condition = filter.measureConditions().get(index); + for (int index = 0; index < filter.getMeasureConditions().size(); index++) { + MeasureFilterCondition condition = filter.getMeasureConditions().get(index); sql.append(", ").append(nullSelect(condition.metric())).append(" crit_").append(index); } sql.append(" FROM snapshots s INNER JOIN projects p ON s.project_id=p.id "); @@ -115,14 +115,14 @@ class MeasureFilterSql { appendResourceConditions(); } - private void appendConditionBlock(int conditionIndex, MeasureFilterValueCondition condition) { + private void appendConditionBlock(int conditionIndex, MeasureFilterCondition condition) { sql.append(" SELECT s.id, s.project_id rid, s.root_project_id rootid, null sortval"); - for (int j = 0; j < filter.measureConditions().size(); j++) { + for (int j = 0; j < filter.getMeasureConditions().size(); j++) { sql.append(", "); if (j == conditionIndex) { sql.append(condition.valueColumn()); } else { - sql.append(nullSelect(filter.measureConditions().get(j).metric())); + sql.append(nullSelect(filter.getMeasureConditions().get(j).metric())); } sql.append(" crit_").append(j); } @@ -130,44 +130,46 @@ class MeasureFilterSql { sql.append(" WHERE "); appendResourceConditions(); sql.append(" AND pm.rule_id IS NULL AND pm.rule_priority IS NULL AND pm.characteristic_id IS NULL AND pm.person_id IS NULL AND "); - condition.appendSql(sql); + condition.appendSqlCondition(sql); } private void appendResourceConditions() { sql.append(" s.status='P' AND s.islast=").append(database.getDialect().getTrueSqlValue()); - sql.append(" AND p.copy_resource_id IS NULL "); - if (!filter.resourceQualifiers().isEmpty()) { + if (context.getBaseSnapshot() == null) { + sql.append(" AND p.copy_resource_id IS NULL "); + } + if (!filter.getResourceQualifiers().isEmpty()) { sql.append(" AND s.qualifier IN "); - appendInStatement(filter.resourceQualifiers(), sql); + appendInStatement(filter.getResourceQualifiers(), sql); } - if (!filter.resourceScopes().isEmpty()) { + if (!filter.getResourceScopes().isEmpty()) { sql.append(" AND s.scope IN "); - appendInStatement(filter.resourceScopes(), sql); + appendInStatement(filter.getResourceScopes(), sql); } - if (!filter.resourceLanguages().isEmpty()) { + if (!filter.getResourceLanguages().isEmpty()) { sql.append(" AND p.language IN "); - appendInStatement(filter.resourceLanguages(), sql); + appendInStatement(filter.getResourceLanguages(), sql); } - if (filter.fromDate() != null) { + if (filter.getFromDate() != null) { sql.append(" AND s.created_at >= ? "); - dateParameters.add(new java.sql.Date(filter.fromDate().getTime())); + dateParameters.add(new java.sql.Date(filter.getFromDate().getTime())); } - if (filter.toDate() != null) { + if (filter.getToDate() != null) { sql.append(" AND s.created_at <= ? "); - dateParameters.add(new java.sql.Date(filter.toDate().getTime())); + dateParameters.add(new java.sql.Date(filter.getToDate().getTime())); } - if (filter.userFavourites() && context.getUserId() != null) { + if (filter.isOnFavourites() && context.getUserId() != null) { sql.append(" AND s.project_id IN (SELECT props.resource_id FROM properties props WHERE props.prop_key='favourite' AND props.user_id="); sql.append(context.getUserId()); sql.append(" AND props.resource_id IS NOT NULL) "); } - if (StringUtils.isNotBlank(filter.resourceName())) { + if (StringUtils.isNotBlank(filter.getResourceName())) { 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.resourceName()))); + sql.append(StringEscapeUtils.escapeSql(StringUtils.lowerCase(filter.getResourceName()))); sql.append("%'"); - if (!filter.resourceQualifiers().isEmpty()) { + if (!filter.getResourceQualifiers().isEmpty()) { sql.append(" AND rindex.qualifier IN "); - appendInStatement(filter.resourceQualifiers(), sql); + appendInStatement(filter.getResourceQualifiers(), sql); } sql.append(") "); } @@ -178,7 +180,7 @@ class MeasureFilterSql { } else { Long rootSnapshotId = (baseSnapshot.getRootId() != null ? baseSnapshot.getRootId() : baseSnapshot.getId()); sql.append(" AND s.root_snapshot_id=").append(rootSnapshotId); - sql.append(" AND s.path LIKE '").append(baseSnapshot.getPath()).append(baseSnapshot.getId()).append(".%'"); + sql.append(" AND s.path LIKE '").append(StringUtils.defaultString(baseSnapshot.getPath())).append(baseSnapshot.getId()).append(".%'"); } } } diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MesasureFilterContext.java b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterContextTest.java similarity index 61% rename from sonar-core/src/main/java/org/sonar/core/measure/MesasureFilterContext.java rename to sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterContextTest.java index c5362a3cdba..ffd7fd643e2 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/MesasureFilterContext.java +++ b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterContextTest.java @@ -19,25 +19,23 @@ */ package org.sonar.core.measure; -import org.sonar.core.resource.SnapshotDto; +import org.junit.Test; -public class MesasureFilterContext { - private Long userId; - private SnapshotDto baseSnapshot; +import static org.fest.assertions.Assertions.assertThat; - public Long getUserId() { - return userId; +public class MeasureFilterContextTest { + @Test + public void test_empty_toString() { + MeasureFilterContext context = new MeasureFilterContext(); + assertThat(context.toString()).isNotEmpty(); } - public void setUserId(Long userId) { - this.userId = userId; - } - - public SnapshotDto getBaseSnapshot() { - return baseSnapshot; - } - - public void setBaseSnapshot(SnapshotDto baseSnapshot) { - this.baseSnapshot = baseSnapshot; + @Test + public void test_toString() { + MeasureFilterContext context = new MeasureFilterContext(); + context.setJson("{}"); + context.setSql("SELECT *"); + context.setUserId(50L); + assertThat(context.toString()).isEqualTo("MeasureFilterContext[json={},sql=SELECT *,user=50]"); } } diff --git a/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterDecoderTest.java b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterDecoderTest.java new file mode 100644 index 00000000000..750cd6667d0 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterDecoderTest.java @@ -0,0 +1,121 @@ +/* + * 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.json.simple.parser.ParseException; +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.MetricFinder; + +import java.util.Date; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MeasureFilterDecoderTest { + + private MetricFinder metricFinder; + + @Before + public void before() { + metricFinder = mock(MetricFinder.class); + when(metricFinder.findByKey(anyString())).thenAnswer(new Answer() { + public Metric answer(InvocationOnMock invocationOnMock) throws Throwable { + return new Metric((String) invocationOnMock.getArguments()[0]); + } + }); + } + + @Test + public void should_decode() throws ParseException { + String json = "{\"base\": \"org.struts\", \"onBaseChildren\": true, \"scopes\": [\"PRJ\"], " + + "\"qualifiers\": [\"TRK\",\"CLA\"], " + + "\"languages\": [\"java\", \"php\"], \"name\": \"Struts\", \"fromDate\": \"2012-12-25\", " + + "\"toDate\": \"2013-01-31\", " + + "\"favourites\": true, " + + "\"sortAsc\": true, \"sortField\": \"METRIC\", \"sortMetric\": \"ncloc\", \"sortPeriod\":5, " + + "\"conditions\":[{\"metric\":\"lines\", \"op\":\">\", \"val\":123.0}]}"; + + MeasureFilter filter = new MeasureFilterDecoder(metricFinder).decode(json); + + assertThat(filter.getBaseResourceKey()).isEqualTo("org.struts"); + assertThat(filter.isOnBaseResourceChildren()).isTrue(); + assertThat(filter.getResourceScopes()).containsExactly("PRJ"); + assertThat(filter.getResourceQualifiers()).containsExactly("TRK", "CLA"); + assertThat(filter.getResourceLanguages()).containsExactly("java", "php"); + assertThat(filter.getResourceName()).isEqualTo("Struts"); + assertThat(filter.getFromDate().getYear()).isEqualTo(2012 - 1900); + assertThat(filter.getToDate().getYear()).isEqualTo(2013 - 1900); + assertThat(filter.isOnFavourites()).isTrue(); + assertThat(filter.sort().metric().getKey()).isEqualTo("ncloc"); + assertThat(filter.sort().isAsc()).isTrue(); + MeasureFilterCondition condition = filter.getMeasureConditions().get(0); + assertThat(condition.metric().getKey()).isEqualTo("lines"); + assertThat(condition.operator()).isEqualTo(">"); + assertThat(condition.value()).isEqualTo(123.0); + } + + @Test + public void should_set_max_date_by_number_of_days() throws ParseException { + String json = "{\"beforeDays\": 5}"; + + MeasureFilter filter = new MeasureFilterDecoder(metricFinder).decode(json); + + assertThat(filter.getFromDate()).isNull(); + assertThat(filter.getToDate().before(new Date())).isTrue(); + } + + @Test + public void should_set_min_date_by_number_of_days() throws ParseException { + String json = "{\"afterDays\": 5}"; + + MeasureFilter filter = new MeasureFilterDecoder(metricFinder).decode(json); + + assertThat(filter.getToDate()).isNull(); + assertThat(filter.getFromDate().before(new Date())).isTrue(); + } + + @Test + public void test_default_values() throws ParseException { + MeasureFilter filter = new MeasureFilterDecoder(metricFinder).decode("{}"); + + assertThat(filter.getBaseResourceKey()).isNull(); + assertThat(filter.isOnBaseResourceChildren()).isFalse(); + assertThat(filter.getResourceScopes()).isEmpty(); + assertThat(filter.getResourceQualifiers()).isEmpty(); + assertThat(filter.getResourceLanguages()).isEmpty(); + assertThat(filter.getResourceName()).isNull(); + assertThat(filter.getFromDate()).isNull(); + assertThat(filter.getToDate()).isNull(); + assertThat(filter.isOnFavourites()).isFalse(); + assertThat(filter.sort().metric()).isNull(); + assertThat(filter.sort().getPeriod()).isNull(); + assertThat(filter.sort().onMeasures()).isFalse(); + assertThat(filter.sort().field()).isEqualTo(MeasureFilterSort.Field.NAME); + assertThat(filter.sort().isAsc()).isTrue(); + assertThat(filter.getMeasureConditions()).isEmpty(); + + } +} 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 new file mode 100644 index 00000000000..b92d173062f --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/measure/MeasureFilterEngineTest.java @@ -0,0 +1,55 @@ +/* + * 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.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.junit.Test; +import org.slf4j.Logger; + +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.*; + +public class MeasureFilterEngineTest { + @Test + public void should_decode_json_and_execute_filter() throws Exception { + MeasureFilterDecoder decoder = mock(MeasureFilterDecoder.class); + MeasureFilter filter = new MeasureFilter(); + when(decoder.decode("{}")).thenReturn(filter); + MeasureFilterExecutor executor = mock(MeasureFilterExecutor.class); + Logger logger = mock(Logger.class); + when(logger.isDebugEnabled()).thenReturn(true); + + MeasureFilterEngine engine = new MeasureFilterEngine(decoder, executor); + + final long userId = 50L; + engine.execute("{}", userId, logger); + verify(executor).execute(refEq(filter), argThat(new BaseMatcher() { + public boolean matches(Object o) { + MeasureFilterContext context = (MeasureFilterContext) o; + return "{}".equals(context.getJson()) && context.getUserId() == userId; + } + + public void describeTo(Description description) { + } + })); + verify(logger).debug(anyString()); + } +} 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 75922b5eea2..e2eb14507bf 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 @@ -27,6 +27,7 @@ import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.SnapshotDto; +import java.sql.SQLException; import java.util.List; import static org.fest.assertions.Assertions.assertThat; @@ -54,15 +55,15 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void invalid_filter_should_not_return_results() { - MeasureFilter filter = new MeasureFilter(); - // no qualifiers - assertThat(executor.execute(filter, null)).isEmpty(); + public void invalid_filter_should_not_return_results() throws SQLException { + MeasureFilter filter = new MeasureFilter().setUserFavourites(true); + // anonymous user does not have favourites + assertThat(executor.execute(filter, new MeasureFilterContext())).isEmpty(); } @Test public void filter_is_not_valid_if_missing_base_snapshot() { - MesasureFilterContext context = new MesasureFilterContext(); + MeasureFilterContext context = new MeasureFilterContext(); MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setOnBaseResourceChildren(true); assertThat(MeasureFilterExecutor.isValid(filter, context)).isFalse(); @@ -70,9 +71,23 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { assertThat(MeasureFilterExecutor.isValid(filter, context)).isTrue(); } + @Test + public void filter_is_not_valid_if_condition_on_unknown_metric() { + MeasureFilterContext context = new MeasureFilterContext(); + MeasureFilter filter = new MeasureFilter().addCondition(new MeasureFilterCondition(null, "<", 3.0)); + assertThat(MeasureFilterExecutor.isValid(filter, context)).isFalse(); + } + + @Test + public void filter_is_not_valid_if_sorting_on_unknown_metric() { + MeasureFilterContext context = new MeasureFilterContext(); + MeasureFilter filter = new MeasureFilter().setSortOnMetric(null); + assertThat(MeasureFilterExecutor.isValid(filter, context)).isFalse(); + } + @Test public void filter_is_not_valid_if_anonymous_favourites() { - MesasureFilterContext context = new MesasureFilterContext(); + MeasureFilterContext context = new MeasureFilterContext(); MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setUserFavourites(true); assertThat(MeasureFilterExecutor.isValid(filter, context)).isFalse(); @@ -81,9 +96,9 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void projects_without_measure_conditions() { + public void projects_without_measure_conditions() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOn(MeasureFilterSort.Field.LANGUAGE); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2); verifyJavaProject(rows.get(0)); @@ -100,9 +115,9 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void sort_by_ascending_resource_name() { + public void sort_by_ascending_resource_name() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortAsc(true); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); // Big -> Tiny assertThat(rows).hasSize(2); @@ -111,9 +126,9 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void sort_by_descending_resource_name() { + public void sort_by_descending_resource_name() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortAsc(false); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); // Tiny -> Big assertThat(rows).hasSize(2); @@ -122,9 +137,9 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void sort_by_ascending_text_measure() { + public void sort_by_ascending_text_measure() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOnMetric(METRIC_PROFILE); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2); verifyPhpProject(rows.get(0));//php way @@ -132,9 +147,9 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void sort_by_descending_text_measure() { + public void sort_by_descending_text_measure() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOnMetric(METRIC_PROFILE).setSortAsc(false); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2); verifyJavaProject(rows.get(0));// Sonar way @@ -142,18 +157,18 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void sort_by_missing_text_measure() { + 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); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2);//2 files randomly sorted } @Test - public void sort_by_ascending_numeric_measure() { + public void sort_by_ascending_numeric_measure() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortOnMetric(METRIC_LINES); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); // Tiny -> Big assertThat(rows).hasSize(2); @@ -162,9 +177,9 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void sort_by_descending_numeric_measure() { + public void sort_by_descending_numeric_measure() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortOnMetric(METRIC_LINES).setSortAsc(false); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); // Big -> Tiny assertThat(rows).hasSize(2); @@ -173,19 +188,19 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void sort_by_missing_numeric_measure() { + public void sort_by_missing_numeric_measure() throws SQLException { // coverage measures are not computed MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setSortOnMetric(METRIC_COVERAGE); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); // 2 files, random order assertThat(rows).hasSize(2); } @Test - public void sort_by_ascending_variation() { + public void sort_by_ascending_variation() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOnMetric(METRIC_LINES).setSortOnPeriod(5); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2); verifyJavaProject(rows.get(0));// +400 @@ -193,10 +208,10 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void sort_by_descending_variation() { + public void sort_by_descending_variation() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK") .setSortOnMetric(METRIC_LINES).setSortOnPeriod(5).setSortAsc(false); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2); verifyPhpProject(rows.get(0));// +4900 @@ -204,70 +219,70 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void sort_by_ascending_date() { + public void sort_by_ascending_date() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOn(MeasureFilterSort.Field.DATE); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); verifyJavaProject(rows.get(0));// 2008 verifyPhpProject(rows.get(1));// 2012 } @Test - public void sort_by_descending_date() { + public void sort_by_descending_date() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setSortOn(MeasureFilterSort.Field.DATE).setSortAsc(false); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); verifyPhpProject(rows.get(0));// 2012 verifyJavaProject(rows.get(1));// 2008 } @Test - public void condition_on_numeric_measure() { + public void condition_on_numeric_measure() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA") .setSortOnMetric(METRIC_LINES) - .addCondition(new MeasureFilterValueCondition(METRIC_LINES, MeasureFilterValueCondition.Operator.GREATER, 200)); - List rows = executor.execute(filter, null); + .addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 200)); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); verifyJavaBigFile(rows.get(0)); } @Test - public void condition_on_measure_variation() { + public void condition_on_measure_variation() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK") .setSortOnMetric(METRIC_LINES) - .addCondition(new MeasureFilterValueCondition(METRIC_LINES, MeasureFilterValueCondition.Operator.GREATER, 1000).setPeriod(5)); - List rows = executor.execute(filter, null); + .addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 1000).setPeriod(5)); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); verifyPhpProject(rows.get(0)); } @Test - public void multiple_conditions_on_numeric_measures() { + public void multiple_conditions_on_numeric_measures() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA") .setSortOnMetric(METRIC_LINES) - .addCondition(new MeasureFilterValueCondition(METRIC_LINES, MeasureFilterValueCondition.Operator.GREATER, 2)) - .addCondition(new MeasureFilterValueCondition(METRIC_LINES, MeasureFilterValueCondition.Operator.LESS_OR_EQUALS, 50)); - List rows = executor.execute(filter, null); + .addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 2)) + .addCondition(new MeasureFilterCondition(METRIC_LINES, "<=", 50)); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); verifyJavaTinyFile(rows.get(0)); } @Test - public void filter_by_language() { + public void filter_by_language() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setResourceLanguages("java", "cobol"); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); verifyJavaProject(rows.get(0)); } @Test - public void filter_by_min_date() { + public void filter_by_min_date() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setFromDate(DateUtils.parseDate("2012-12-13")); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); // php has been analyzed in 2012-12-13, whereas java project has been analyzed in 2008 assertThat(rows).hasSize(1); @@ -275,11 +290,11 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void filter_by_range_of_dates() { + public void filter_by_range_of_dates() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK") .setFromDate(DateUtils.parseDate("2007-01-01")) .setToDate(DateUtils.parseDate("2010-01-01")); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); // php has been analyzed in 2012-12-13, whereas java project has been analyzed in 2008 assertThat(rows).hasSize(1); @@ -287,18 +302,18 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void filter_by_resource_name() { + public void filter_by_resource_name() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK").setResourceName("PHP Proj"); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1); verifyPhpProject(rows.get(0)); } @Test - public void filter_by_base_resource() { + public void filter_by_base_resource() throws SQLException { MeasureFilter filter = new MeasureFilter().setResourceQualifiers("CLA").setBaseResourceKey("java_project"); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(2); // default sort is on resource name @@ -307,26 +322,26 @@ public class MeasureFilterExecutorTest extends AbstractDaoTestCase { } @Test - public void filter_by_parent_resource() { - MeasureFilter filter = new MeasureFilter().setResourceQualifiers("TRK", "PAC", "CLA").setBaseResourceKey("java_project").setOnBaseResourceChildren(true); - List rows = executor.execute(filter, null); + public void filter_by_parent_resource() throws SQLException { + MeasureFilter filter = new MeasureFilter().setBaseResourceKey("java_project").setOnBaseResourceChildren(true); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).hasSize(1);// the package org.sonar.foo assertThat(rows.get(0).getSnapshotId()).isEqualTo(JAVA_PACKAGE_SNAPSHOT_ID); } @Test - public void filter_by_parent_without_children() { + 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); - List rows = executor.execute(filter, null); + List rows = executor.execute(filter, new MeasureFilterContext()); assertThat(rows).isEmpty(); } @Test - public void filter_by_user_favourites() { + public void filter_by_user_favourites() throws SQLException { MeasureFilter filter = new MeasureFilter().setUserFavourites(true); - List rows = executor.execute(filter, 50L); + List rows = executor.execute(filter, new MeasureFilterContext().setUserId(50L)); assertThat(rows).hasSize(2); verifyJavaBigFile(rows.get(0)); diff --git a/sonar-server/src/dev/h2/conf/logback.xml b/sonar-server/src/dev/h2/conf/logback.xml index 127e47ad786..3ea98589f88 100644 --- a/sonar-server/src/dev/h2/conf/logback.xml +++ b/sonar-server/src/dev/h2/conf/logback.xml @@ -31,6 +31,10 @@ + + + + diff --git a/sonar-server/src/dev/mysql/conf/logback.xml b/sonar-server/src/dev/mysql/conf/logback.xml index 127e47ad786..3ea98589f88 100644 --- a/sonar-server/src/dev/mysql/conf/logback.xml +++ b/sonar-server/src/dev/mysql/conf/logback.xml @@ -31,6 +31,10 @@ + + + + diff --git a/sonar-server/src/dev/postgresql/conf/logback.xml b/sonar-server/src/dev/postgresql/conf/logback.xml index 127e47ad786..3ea98589f88 100644 --- a/sonar-server/src/dev/postgresql/conf/logback.xml +++ b/sonar-server/src/dev/postgresql/conf/logback.xml @@ -31,6 +31,10 @@ + + + + diff --git a/sonar-server/src/main/java/org/sonar/server/filters/Filter.java b/sonar-server/src/main/java/org/sonar/server/filters/Filter.java deleted file mode 100644 index 351477d93ed..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/filters/Filter.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * 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.server.filters; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.builder.ReflectionToStringBuilder; -import org.apache.commons.lang.builder.ToStringStyle; -import org.sonar.api.resources.Qualifiers; - -import java.util.List; -import java.util.Set; - -public class Filter { - - // path - private Integer rootSnapshotId; - private Integer baseSnapshotId; - private String baseSnapshotPath; - - // filters on resources - private Set scopes; - private Set qualifiers; - private Set languages; - private Set favouriteIds; - private DateCriterion dateCriterion; - private String keyRegexp; - private String nameRegexp; - private boolean onDirectChildren = false; - - // filters on measures - private List measureCriteria = Lists.newLinkedList(); - private int periodIndex = 0; - - // sorting - private Integer sortedMetricId; - private Boolean sortedByMeasureVariation = Boolean.FALSE; - private boolean sortedByLanguage; - private boolean sortedByName; - private boolean sortedByKey; - private boolean sortedByDate; - private boolean sortedByVersion; - private boolean isNumericMetric = true; - private boolean ascendingSort = true; - - public Filter setPath(Integer rootSnapshotId, Integer snapshotId, String snapshotPath) { - this.baseSnapshotId = snapshotId; - if (rootSnapshotId == null) { - this.rootSnapshotId = snapshotId; - } else { - this.rootSnapshotId = rootSnapshotId; - } - this.baseSnapshotPath = StringUtils.defaultString(snapshotPath, ""); //With Oracle the path can be null (see SONAR-2582) - return this; - } - - public Integer getRootSnapshotId() { - return rootSnapshotId; - } - - public boolean hasBaseSnapshot() { - return baseSnapshotId != null; - } - - public Integer getBaseSnapshotId() { - return baseSnapshotId; - } - - public String getBaseSnapshotPath() { - return baseSnapshotPath; - } - - public Set getScopes() { - return scopes; - } - - public boolean hasScopes() { - return scopes != null && !scopes.isEmpty(); - } - - public Filter setScopes(Set scopes) { - this.scopes = scopes; - return this; - } - - public Filter setScopes(String... scopes) { - this.scopes = Sets.newHashSet(scopes); - return this; - } - - public Set getQualifiers() { - return qualifiers; - } - - public boolean hasQualifiers() { - return qualifiers != null && !qualifiers.isEmpty(); - } - - public Filter setQualifiers(Set qualifiers) { - this.qualifiers = qualifiers; - return this; - } - - public Filter setQualifiers(String... qualifiers) { - this.qualifiers = Sets.newHashSet(qualifiers); - return this; - } - - public Set getLanguages() { - return languages; - } - - public boolean hasLanguages() { - return languages != null && !languages.isEmpty(); - } - - public Filter setLanguages(Set languages) { - this.languages = languages; - return this; - } - - public Filter setLanguages(String... languages) { - this.languages = Sets.newHashSet(languages); - return this; - } - - public Set getFavouriteIds() { - return favouriteIds; - } - - public boolean hasFavouriteIds() { - return favouriteIds != null && !favouriteIds.isEmpty(); - } - - public Filter setFavouriteIds(Set favouriteIds) { - this.favouriteIds = favouriteIds; - return this; - } - - public Filter setFavouriteIds(Integer... favouriteIds) { - this.favouriteIds = Sets.newHashSet(favouriteIds); - return this; - } - - public Integer getSortedMetricId() { - return sortedMetricId; - } - - public boolean isNumericMetric() { - return isNumericMetric; - } - - public boolean isTextSort() { - return !isNumericMetric || sortedByLanguage || sortedByName || sortedByVersion || sortedByKey; - } - - public Filter setSortedMetricId(Integer id, boolean isNumericValue, Boolean isVariation) { - unsetSorts(); - this.sortedMetricId = id; - this.isNumericMetric = isNumericValue; - this.sortedByMeasureVariation = isVariation; - return this; - } - - public boolean isSortedByLanguage() { - return sortedByLanguage; - } - - public Filter setSortedByLanguage() { - unsetSorts(); - this.sortedByLanguage = true; - return this; - } - - public boolean isSortedByName() { - return sortedByName; - } - - public boolean isSortedByKey() { - return sortedByKey; - } - - public boolean isSortedByVersion() { - return sortedByVersion; - } - - public Filter setSortedByVersion() { - unsetSorts(); - this.sortedByVersion = true; - return this; - } - - public boolean isSorted() { - return isSortedByLanguage() || isSortedByName() || isSortedByKey() || isSortedByDate() || isSortedByVersion() || getSortedMetricId() != null; - } - - public boolean isSortedByDate() { - return sortedByDate; - } - - public Filter setSortedByDate() { - unsetSorts(); - sortedByDate = true; - return this; - } - - public Filter setSortedByName() { - unsetSorts(); - this.sortedByName = true; - return this; - } - - public Filter setSortedByKey() { - unsetSorts(); - this.sortedByKey = true; - return this; - } - - private void unsetSorts() { - this.sortedByDate = false; - this.sortedByLanguage = false; - this.sortedByName = false; - this.sortedByKey = false; - this.sortedMetricId = null; - this.sortedByVersion = false; - this.isNumericMetric = true; - } - - public List getMeasureCriteria() { - return measureCriteria; - } - - public Filter setMeasureCriteria(List l) { - this.measureCriteria = l; - return this; - } - - public Filter addMeasureCriterion(MeasureCriterion c) { - this.measureCriteria.add(c); - return this; - } - - public Filter createMeasureCriterionOnValue(Integer metricId, String operator, Double value, Boolean variation) { - this.measureCriteria.add(new MeasureCriterion(metricId, operator, value, variation)); - return this; - } - - public boolean hasMeasureCriteria() { - return !measureCriteria.isEmpty(); - } - - protected boolean hasMeasureCriteriaOnMetric(Integer metricId) { - if (metricId != null) { - for (MeasureCriterion criterion : measureCriteria) { - if (metricId.equals(criterion.getMetricId())) { - return true; - } - } - } - return false; - } - - public boolean mustJoinMeasuresTable() { - return sortedMetricId != null || hasMeasureCriteria(); - } - - public boolean isAscendingSort() { - return ascendingSort; - } - - public Filter setAscendingSort(boolean b) { - this.ascendingSort = b; - return this; - } - - public DateCriterion getDateCriterion() { - return dateCriterion; - } - - public Filter setDateCriterion(DateCriterion dc) { - this.dateCriterion = dc; - return this; - } - - public Filter setDateCriterion(String operator, Integer daysAgo) { - this.dateCriterion = new DateCriterion().setOperator(operator).setDate(daysAgo); - return this; - } - - public String getKeyRegexp() { - return keyRegexp; - } - - public Filter setKeyRegexp(String s) { - this.keyRegexp = s; - return this; - } - - public String getNameRegexp() { - return nameRegexp; - } - - public Filter setNameRegexp(String s) { - this.nameRegexp = s; - return this; - } - - public int getPeriodIndex() { - return periodIndex; - } - - public void setPeriodIndex(int i) { - this.periodIndex = i; - } - - public boolean isOnPeriod() { - return periodIndex > 0; - } - - public boolean isOnDirectChildren() { - return onDirectChildren; - } - - public void setOnDirectChildren(boolean b) { - this.onDirectChildren = b; - } - - static String getVariationColumn(int periodIndex) { - switch (periodIndex) { - case 1: - return "variation_value_1"; - case 2: - return "variation_value_2"; - case 3: - return "variation_value_3"; - case 4: - return "variation_value_4"; - case 5: - return "variation_value_5"; - default: - return null; - } - } - - String getColumnToSort() { - String col = "text_value"; - if (isNumericMetric()) { - col = (sortedByMeasureVariation == Boolean.TRUE ? getVariationColumn(periodIndex) : "value"); - } - return col; - } - - public boolean mustReturnEmptyResult() { - boolean hasCriterionOnVariation = false; - for (MeasureCriterion criterion : measureCriteria) { - if (criterion.isVariation() == Boolean.TRUE) { - hasCriterionOnVariation = true; - } - } - return (hasCriterionOnVariation && !isOnPeriod()); - } - - @Override - public String toString() { - return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE); - } - - public static Filter createForAllQualifiers() { - return new Filter().setQualifiers( - Qualifiers.VIEW, Qualifiers.SUBVIEW, - Qualifiers.PROJECT, Qualifiers.MODULE, Qualifiers.DIRECTORY, Qualifiers.PACKAGE, - Qualifiers.FILE, Qualifiers.CLASS, Qualifiers.UNIT_TEST_FILE, Qualifiers.LIBRARY, Qualifiers.PARAGRAPH); - } -} diff --git a/sonar-server/src/main/java/org/sonar/server/filters/FilterExecutor.java b/sonar-server/src/main/java/org/sonar/server/filters/FilterExecutor.java deleted file mode 100644 index c3d0f26e2fb..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/filters/FilterExecutor.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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.server.filters; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.ServerComponent; -import org.sonar.api.database.DatabaseSession; -import org.sonar.api.database.model.Snapshot; -import org.sonar.api.utils.SonarException; -import org.sonar.api.utils.TimeProfiler; -import org.sonar.core.persistence.Database; -import org.sonar.core.persistence.dialect.Dialect; -import org.sonar.core.persistence.dialect.MsSql; - -import javax.persistence.Query; - -import java.util.Collections; - -public class FilterExecutor implements ServerComponent { - private static final Logger LOG = LoggerFactory.getLogger(FilterExecutor.class); - private static final int SQL_INITIAL_SIZE = 1000; - private DatabaseSession session; - private Dialect dialect; - - public FilterExecutor(DatabaseSession session, Database database) { - this(session, database.getDialect()); - } - - @VisibleForTesting - FilterExecutor(DatabaseSession session, Dialect dialect) { - this.session = session; - this.dialect = dialect; - } - - public FilterResult execute(Filter filter) { - if (filter.mustReturnEmptyResult()) { - return new FilterResult(filter, Collections. emptyList()); - } - - String sql = null; - try { - TimeProfiler profiler = new TimeProfiler(FilterExecutor.class).setLevelToDebug().start("Build/execute SQL query"); - sql = toSql(filter); - LOG.debug("SQL: " + sql); - Query query = session.getEntityManager().createNativeQuery(sql); - setHqlParameters(filter, query); - FilterResult result = new FilterResult(filter, query.getResultList()); - profiler.stop(); - - profiler.start("Process rows"); - result.removeUnvalidRows(); - profiler.stop(); - - profiler.start("Sort rows"); - result.sort(); - profiler.stop(); - return result; - - } catch (Exception e) { - throw new SonarException("Fail to execute filter: " + filter.toString() + ", sql=" + sql, e); - } - } - - @VisibleForTesting - String toSql(Filter filter) { - StringBuilder sql = new StringBuilder(SQL_INITIAL_SIZE); - addSelectColumns(filter, sql); - addFromClause(filter, sql); - addWhereClause(filter, sql); - return sql.toString(); - } - - private void addSelectColumns(Filter filter, StringBuilder sql) { - sql.append("SELECT s.id, MAX(s.project_id) as pid, MAX(s.root_project_id) as rpid"); - if (filter.isSortedByLanguage()) { - sql.append(", MAX(p.language) as lang "); - - } else if (filter.isSortedByName()) { - sql.append(", MAX(p.long_name) as name "); - - } else if (filter.isSortedByKey()) { - sql.append(", MAX(p.kee) as kee "); - - } else if (filter.isSortedByDate()) { - sql.append(", MAX(s.created_at) as createdat "); - - } else if (filter.isSortedByVersion()) { - sql.append(", MAX(s.version) as version "); - } - if (filter.getSortedMetricId() != null) { - sql.append(", MAX(CASE WHEN pm.metric_id="); - sql.append(filter.getSortedMetricId()); - sql.append(" THEN "); - sql.append(filter.getColumnToSort()); - sql.append(" ELSE NULL END) AS sortvalue"); - sql.append(" "); - } - for (int index = 0; index < filter.getMeasureCriteria().size(); index++) { - MeasureCriterion criterion = filter.getMeasureCriteria().get(index); - String column = (criterion.isVariation() ? Filter.getVariationColumn(filter.getPeriodIndex()) : "value"); - sql.append(", MAX(CASE WHEN pm.metric_id="); - sql.append(criterion.getMetricId()); - sql.append(" AND pm."); - sql.append(column); - sql.append(criterion.getOperator()); - sql.append(criterion.getValue()); - sql.append(" THEN "); - sql.append(column); - sql.append(" ELSE NULL END) AS crit_"); - sql.append(index); - sql.append(" "); - } - } - - private void addFromClause(Filter filter, StringBuilder sql) { - sql.append(" FROM snapshots s "); - if (filter.mustJoinMeasuresTable()) { - sql.append(" INNER JOIN project_measures pm "); - if (MsSql.ID.equals(dialect.getId())) { - // SONAR-3422 - sql.append(" WITH (INDEX(measures_sid_metric)) "); - } - sql.append(" ON s.id=pm.snapshot_id "); - } - sql.append(" INNER JOIN projects p ON s.project_id=p.id "); - } - - private void addWhereClause(Filter filter, StringBuilder sql) { - sql.append(" WHERE "); - if (filter.mustJoinMeasuresTable()) { - if (filter.hasMeasureCriteria()) { - sql.append(" ( "); - int index = 0; - while (index < filter.getMeasureCriteria().size()) { - if (index > 0) { - sql.append(" OR "); - } - MeasureCriterion criterion = filter.getMeasureCriteria().get(index); - String column = (criterion.isVariation() ? Filter.getVariationColumn(filter.getPeriodIndex()) : "value"); - sql.append("(pm.metric_id=").append(criterion.getMetricId()).append(" and pm.").append(column) - .append(criterion.getOperator()).append(criterion.getValue()).append(")"); - index++; - } - - if (filter.getSortedMetricId() != null && !filter.hasMeasureCriteriaOnMetric(filter.getSortedMetricId())) { - sql.append(" OR (pm.metric_id=").append(filter.getSortedMetricId()).append(") "); - } - - sql.append(" ) AND "); - } - sql.append(" pm.rule_id IS NULL AND pm.rule_priority IS NULL"); - sql.append(" AND pm.characteristic_id IS NULL"); - sql.append(" AND pm.person_id IS NULL"); - sql.append(" AND "); - } - sql.append(" s.status=:status AND s.islast=:islast "); - if (filter.getScopes() != null) { - sql.append(filter.getScopes().isEmpty() ? " AND s.scope IS NULL " : " AND s.scope IN (:scopes) "); - } - if (filter.hasQualifiers()) { - sql.append(" AND s.qualifier IN (:qualifiers) "); - } else if (!filter.isOnDirectChildren()) { - // no criteria -> we should not display all rows but no rows - sql.append(" AND s.qualifier IS NULL "); - } - if (filter.hasLanguages()) { - sql.append(" AND p.language IN (:languages) "); - } - if (filter.getFavouriteIds() != null) { - sql.append(filter.getFavouriteIds().isEmpty() ? " AND s.project_id IS NULL " : " AND s.project_id IN (:favourites) "); - } - if (filter.hasBaseSnapshot()) { - if (filter.isOnDirectChildren()) { - sql.append(" AND s.parent_snapshot_id=:parent_sid "); - } else { - sql.append(" AND s.root_snapshot_id=:root_sid AND s.path LIKE :path "); - } - } - if (filter.getDateCriterion() != null) { - sql.append(" AND s.created_at"); - sql.append(filter.getDateCriterion().getOperator()); - sql.append(" :date "); - } - if (StringUtils.isNotBlank(filter.getKeyRegexp())) { - sql.append(" AND UPPER(p.kee) LIKE :kee"); - } - if (StringUtils.isNotBlank(filter.getNameRegexp())) { - sql.append(" AND UPPER(p.long_name) LIKE :name"); - } - if (!filter.hasBaseSnapshot()) { - sql.append(" AND p.copy_resource_id IS NULL "); - } - sql.append(" GROUP BY s.id"); - } - - private void setHqlParameters(Filter filter, Query query) { - query.setParameter("status", Snapshot.STATUS_PROCESSED); - query.setParameter("islast", true); - if (filter.hasScopes()) { - query.setParameter("scopes", filter.getScopes()); - } - if (filter.hasQualifiers()) { - query.setParameter("qualifiers", filter.getQualifiers()); - } - if (filter.hasLanguages()) { - query.setParameter("languages", filter.getLanguages()); - } - if (filter.hasFavouriteIds()) { - query.setParameter("favourites", filter.getFavouriteIds()); - } - if (filter.getDateCriterion() != null) { - query.setParameter("date", filter.getDateCriterion().getDate()); - } - if (filter.hasBaseSnapshot()) { - if (filter.isOnDirectChildren()) { - query.setParameter("parent_sid", filter.getBaseSnapshotId()); - } else { - query.setParameter("root_sid", filter.getRootSnapshotId()); - query.setParameter("path", new StringBuilder().append( - filter.getBaseSnapshotPath()).append(filter.getBaseSnapshotId()).append(".%").toString()); - } - } - if (StringUtils.isNotBlank(filter.getKeyRegexp())) { - query.setParameter("kee", StringUtils.upperCase(StringUtils.replaceChars(filter.getKeyRegexp(), '*', '%'))); - } - if (StringUtils.isNotBlank(filter.getNameRegexp())) { - query.setParameter("name", StringUtils.upperCase(StringUtils.replaceChars(filter.getNameRegexp(), '*', '%'))); - } - } -} diff --git a/sonar-server/src/main/java/org/sonar/server/filters/FilterResult.java b/sonar-server/src/main/java/org/sonar/server/filters/FilterResult.java deleted file mode 100644 index 87987537c0f..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/filters/FilterResult.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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.server.filters; - -import com.google.common.collect.Ordering; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -public class FilterResult { - private List rows; - private Filter filter; - public static final int SORTED_COLUMN_INDEX = 3; - - public FilterResult(Filter filter, List rows) { - this.rows = new ArrayList(rows); - this.filter = filter; - } - - /** - * @return a list of arrays - */ - public List getRows() { - return rows; - } - - public int size() { - return rows.size(); - } - - public Integer getSnapshotId(Object[] row) { - return (Integer) row[getSnapshotIdIndex()]; - } - - public Integer getProjectId(Object[] row) { - return (Integer) row[getProjectIdIndex()]; - } - - public Integer getRootProjectId(Object[] row) { - return (Integer) row[getRootProjectIdIndex()]; - } - - public int getSnapshotIdIndex() { - return 0; - } - - public int getProjectIdIndex() { - return 1; - } - - public int getRootProjectIdIndex() { - return 2; - } - - public void sort() { - if (filter.isSorted()) { - Comparator comparator = (filter.isTextSort() ? new StringIgnoreCaseComparator(SORTED_COLUMN_INDEX) : new NumericComparator(SORTED_COLUMN_INDEX)); - if (!filter.isAscendingSort()) { - comparator = Ordering.from(comparator).reverse(); - } - Collections.sort(rows, comparator); - } - } - - public void removeUnvalidRows() { - int numberOfCriteria = filter.getMeasureCriteria().size(); - if (numberOfCriteria > 0) { - int fromColumnIndex = (filter.isSorted() ? SORTED_COLUMN_INDEX + 1 : SORTED_COLUMN_INDEX); - for (Iterator it = rows.iterator(); it.hasNext(); ) { - Object[] row = it.next(); - boolean remove = false; - for (int index = 0; index < numberOfCriteria; index++) { - if (row[fromColumnIndex + index] == null) { - remove = true; - } - } - if (remove) { - it.remove(); - } - } - } - } - - static final class NumericComparator implements Comparator, Serializable { - private static final long serialVersionUID = 4627704879575964978L; - private int index; - - NumericComparator(int index) { - this.index = index; - } - - public int compare(Object[] a1, Object[] a2) { - Comparable c1 = (Comparable) a1[index]; - Comparable o2 = (Comparable) a2[index]; - - return (c1 == null ? -1 : (o2 == null ? 1 : c1.compareTo(o2))); - } - } - - static final class StringIgnoreCaseComparator implements Comparator, Serializable { - private static final long serialVersionUID = 963926659201833101L; - private int index; - - StringIgnoreCaseComparator(int index) { - this.index = index; - } - - public int compare(Object[] o1, Object[] o2) { - String s1 = (String) o1[index]; - if (s1 == null) { - return -1; - } - String s2 = (String) o2[index]; - if (s2 == null) { - return 1; - } - return s1.compareToIgnoreCase(s2); - } - } -} - diff --git a/sonar-server/src/main/java/org/sonar/server/filters/MeasureCriterion.java b/sonar-server/src/main/java/org/sonar/server/filters/MeasureCriterion.java deleted file mode 100644 index 2413df514a4..00000000000 --- a/sonar-server/src/main/java/org/sonar/server/filters/MeasureCriterion.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.server.filters; - -import org.apache.commons.lang.builder.ReflectionToStringBuilder; -import org.apache.commons.lang.builder.ToStringStyle; - -public class MeasureCriterion { - - private Integer metricId; - private String operator; - private Double value; - private Boolean variation; - - public MeasureCriterion(Integer metricId, String operator, Double value, Boolean variation) { - this.metricId = metricId; - this.operator = operator; - this.value = value; - this.variation = variation; - } - - public Integer getMetricId() { - return metricId; - } - - public void setMetricId(Integer metricId) { - this.metricId = metricId; - } - - public String getOperator() { - return operator; - } - - public void setOperator(String operator) { - this.operator = operator; - } - - public Double getValue() { - return value; - } - - public void setValue(Double value) { - this.value = value; - } - - public boolean isVariation() { - return variation==Boolean.TRUE; - } - - public MeasureCriterion setVariation(Boolean b) { - this.variation = b; - return this; - } - - @Override - public String toString() { - return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString(); - } -} 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 56ae61b787f..83ae16377ae 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 @@ -42,13 +42,12 @@ import org.sonar.core.config.Logback; import org.sonar.core.i18n.GwtI18n; import org.sonar.core.i18n.I18nManager; 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.metric.DefaultMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; -import org.sonar.core.persistence.DaoUtils; -import org.sonar.core.persistence.DatabaseMigrator; -import org.sonar.core.persistence.DatabaseVersion; -import org.sonar.core.persistence.DefaultDatabase; -import org.sonar.core.persistence.MyBatis; +import org.sonar.core.persistence.*; import org.sonar.core.qualitymodel.DefaultModelFinder; import org.sonar.core.rule.DefaultRuleFinder; import org.sonar.core.user.DefaultUserFinder; @@ -65,25 +64,14 @@ import org.sonar.server.charts.ChartFactory; import org.sonar.server.configuration.Backup; import org.sonar.server.configuration.ProfilesManager; import org.sonar.server.database.EmbeddedDatabaseFactory; -import org.sonar.server.filters.FilterExecutor; import org.sonar.server.notifications.NotificationService; import org.sonar.server.notifications.reviews.ReviewsNotificationManager; -import org.sonar.server.plugins.ApplicationDeployer; -import org.sonar.server.plugins.DefaultServerPluginRepository; -import org.sonar.server.plugins.PluginDeployer; -import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.ServerExtensionInstaller; -import org.sonar.server.plugins.UpdateCenterClient; -import org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.server.plugins.*; import org.sonar.server.qualitymodel.DefaultModelManager; import org.sonar.server.rules.ProfilesConsole; import org.sonar.server.rules.RulesConsole; import org.sonar.server.startup.*; -import org.sonar.server.ui.CodeColorizers; -import org.sonar.server.ui.JRubyI18n; -import org.sonar.server.ui.PageDecorations; -import org.sonar.server.ui.SecurityRealmFactory; -import org.sonar.server.ui.Views; +import org.sonar.server.ui.*; import javax.servlet.ServletContext; @@ -203,7 +191,6 @@ public final class Platform { servicesContainer.addSingleton(UpdateCenterMatrixFactory.class); servicesContainer.addSingleton(PluginDownloader.class); servicesContainer.addSingleton(ServerIdGenerator.class); - servicesContainer.addComponent(FilterExecutor.class, false); servicesContainer.addSingleton(DefaultModelFinder.class); // depends on plugins servicesContainer.addSingleton(DefaultModelManager.class); servicesContainer.addSingleton(Plugins.class); @@ -233,6 +220,9 @@ public final class Platform { servicesContainer.addSingleton(NewUserNotifier.class); servicesContainer.addSingleton(SettingsChangeNotifier.class); servicesContainer.addSingleton(PageDecorations.class); + servicesContainer.addSingleton(MeasureFilterDecoder.class); + servicesContainer.addSingleton(MeasureFilterExecutor.class); + servicesContainer.addSingleton(MeasureFilterEngine.class); // Notifications servicesContainer.addSingleton(EmailSettings.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 b588f82a924..9f52e40f9b1 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 @@ -38,16 +38,14 @@ import org.sonar.api.resources.ResourceTypes; import org.sonar.api.rules.RulePriority; import org.sonar.api.rules.RuleRepository; import org.sonar.api.utils.ValidationMessages; -import org.sonar.api.web.Footer; -import org.sonar.api.web.NavigationSection; -import org.sonar.api.web.Page; -import org.sonar.api.web.RubyRailsWebservice; -import org.sonar.api.web.Widget; +import org.sonar.api.web.*; import org.sonar.api.workflow.Review; import org.sonar.api.workflow.internal.DefaultReview; import org.sonar.api.workflow.internal.DefaultWorkflowContext; import org.sonar.api.workflow.screen.Screen; import org.sonar.core.i18n.RuleI18nManager; +import org.sonar.core.measure.MeasureFilterEngine; +import org.sonar.core.measure.MeasureFilterRow; import org.sonar.core.persistence.Database; import org.sonar.core.persistence.DatabaseMigrator; import org.sonar.core.purge.PurgeDao; @@ -57,26 +55,14 @@ import org.sonar.core.workflow.WorkflowEngine; import org.sonar.markdown.Markdown; import org.sonar.server.configuration.Backup; import org.sonar.server.configuration.ProfilesManager; -import org.sonar.server.filters.Filter; -import org.sonar.server.filters.FilterExecutor; -import org.sonar.server.filters.FilterResult; import org.sonar.server.notifications.reviews.ReviewsNotificationManager; -import org.sonar.server.platform.NewUserNotifier; -import org.sonar.server.platform.Platform; -import org.sonar.server.platform.ServerIdGenerator; -import org.sonar.server.platform.ServerSettings; -import org.sonar.server.platform.SettingsChangeNotifier; -import org.sonar.server.plugins.DefaultServerPluginRepository; -import org.sonar.server.plugins.PluginDeployer; -import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.UpdateCenterMatrix; -import org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.server.platform.*; +import org.sonar.server.plugins.*; import org.sonar.server.rules.ProfilesConsole; import org.sonar.server.rules.RulesConsole; import org.sonar.updatecenter.common.Version; import javax.annotation.Nullable; - import java.net.InetAddress; import java.sql.Connection; import java.util.Collection; @@ -97,8 +83,8 @@ public final class JRubyFacade { return getContainer().getComponentByType(componentType); } - public FilterResult executeFilter(Filter filter) { - return get(FilterExecutor.class).execute(filter); + public List executeMeasureFilter(String json, @Nullable Long userId) throws Exception { + return get(MeasureFilterEngine.class).execute(json, userId); } public Collection getResourceTypesForFilter() { @@ -313,7 +299,7 @@ public final class JRubyFacade { public void ruleSeverityChanged(int parentProfileId, int activeRuleId, int oldSeverityId, int newSeverityId, String userName) { getProfilesManager().ruleSeverityChanged(parentProfileId, activeRuleId, RulePriority.values()[oldSeverityId], - RulePriority.values()[newSeverityId], userName); + RulePriority.values()[newSeverityId], userName); } public void ruleDeactivated(int parentProfileId, int deactivatedRuleId, String userName) { @@ -509,10 +495,10 @@ public final class JRubyFacade { // notifier is null when creating the administrator in the migration script 011. if (notifier != null) { notifier.onNewUser(NewUserHandler.Context.builder() - .setLogin(fields.get("login")) - .setName(fields.get("name")) - .setEmail(fields.get("email")) - .build()); + .setLogin(fields.get("login")) + .setName(fields.get("name")) + .setEmail(fields.get("email")) + .build()); } } } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/filter.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/filter.rb index 07c04795132..471cc457536 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/filter.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/filter.rb @@ -168,7 +168,7 @@ class Filter < ActiveRecord::Base def on_direct_children? if resource_id c = criterion('direct-children') - c && c.text_value=='true' + c ? c.text_value=='true' : false else false end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/models/filter_context.rb b/sonar-server/src/main/webapp/WEB-INF/app/models/filter_context.rb index 060e743aae4..f3346ab7634 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/models/filter_context.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/models/filter_context.rb @@ -135,25 +135,15 @@ private - def extract_snapshot_ids(sql_rows) + def extract_snapshot_ids(filter_rows) sids=[] - project_ids=sql_rows.map{|r| r[2] ? to_integer(r[2]) : to_integer(r[1])}.compact.uniq + project_ids=filter_rows.map{|row| row.getResourceRootId()}.compact.uniq authorized_pids=select_authorized(:user, project_ids) - sql_rows.each do |row| - pid=(row[2] ? to_integer(row[2]) : to_integer(row[1])) - if authorized_pids.include?(pid) - sids< {:project_id => filter.resource_id, :islast => true}) - if snapshot - java_filter.setPath(snapshot.root_snapshot_id, snapshot.id, snapshot.path) - else - java_filter.setPath(-1, -1, '') - end + filter_json[:base]=filter.resource.key end if filter.favourites - java_filter.setFavouriteIds((authenticated_system.current_user.favourite_ids||[]).to_java(:Integer)) + filter_json[:favourites]=true end date_criterion=filter.criterion('date') if date_criterion - java_filter.setDateCriterion(date_criterion.operator, date_criterion.value.to_i) + if date_criterion.operator=='<' + filter_json[:beforeDays]=date_criterion.value.to_i + else + filter_json[:afterDays]=date_criterion.value.to_i + end end key_criterion=filter.criterion('key') if key_criterion - java_filter.setKeyRegexp(key_criterion.text_value) + #java_filter.setKeyRegexp(key_criterion.text_value) end name_criterion=filter.criterion('name') if name_criterion - java_filter.setNameRegexp(name_criterion.text_value) + filter_json[:name]=name_criterion.text_value end qualifier_criterion=filter.criterion('qualifier') if qualifier_criterion - java_filter.setQualifiers(qualifier_criterion.text_values.to_java(:String)) - else - java_filter.setQualifiers([].to_java(:String)) + filter_json[:qualifiers]=qualifier_criterion.text_values end - java_filter.setOnDirectChildren(filter.on_direct_children?) + filter_json[:onBaseChildren]=filter.on_direct_children? language_criterion=filter.criterion('language') if language_criterion - java_filter.setLanguages(language_criterion.text_values.to_java :String) + filter_json[:languages]=language_criterion.text_values end #----- FILTER ON MEASURES - filter.measure_criteria.each do |c| - java_filter.createMeasureCriterionOnValue(c.metric.id, c.operator, c.value, c.variation) + filter_json[:conditions]=filter.measure_criteria.map do |c| + hash = {:metric => c.metric.key, :op => c.operator, :val => c.value} + if c.variation + hash[:period] = filter_context.period_index || -1 + end + hash end #----- SORTED COLUMN - if filter_context.sorted_column_id filter.sorted_column=filter_context.sorted_column_id end if filter.sorted_column - if filter.sorted_column.on_name? - java_filter.setSortedByName() - - elsif filter.sorted_column.on_date? - java_filter.setSortedByDate() - - elsif filter.sorted_column.on_version? - java_filter.setSortedByVersion() - - elsif filter.sorted_column.on_language? - java_filter.setSortedByLanguage() - - elsif filter.sorted_column.on_key? - java_filter.setSortedByKey() - - elsif filter.sorted_column.on_metric? && filter.sorted_column.metric - metric=filter.sorted_column.metric - java_filter.setSortedMetricId(metric.id, metric.numeric?, filter.sorted_column.variation) + filter_json[:sortField]=filter.sorted_column.family.upcase + if filter.sorted_column.on_metric? && filter.sorted_column.metric + filter_json[:sortMetric]=filter.sorted_column.metric.key + if filter.sorted_column.variation + filter_json[:sortPeriod]=filter_context.period_index || -1 + end end end @@ -104,51 +93,39 @@ class Filters #----- SORTING DIRECTION if filter.sorted_column if filter_context.ascending_sort.nil? - java_filter.setAscendingSort(filter.sorted_column.ascending?) + filter_json[:sortAsc]=filter.sorted_column.ascending? else filter.sorted_column.ascending=filter_context.ascending_sort - java_filter.setAscendingSort(filter.sorted_column.ascending?) + filter_json[:sortAsc]=filter.sorted_column.ascending? end if filter_context.ascending_sort filter.sorted_column.ascending=filter_context.ascending_sort end - java_filter.setAscendingSort(filter.sorted_column.ascending?) + filter_json[:sortAsc]=filter.sorted_column.ascending? end - #----- VARIATION - java_filter.setPeriodIndex(filter_context.period_index) - #----- EXECUTION - java_result=Java::OrgSonarServerUi::JRubyFacade.getInstance().execute_filter(java_filter) - snapshot_ids=extract_snapshot_ids(java_result.getRows(), authenticated_system) + user=authenticated_system.current_user + rows=Api::Utils.java_facade.executeMeasureFilter(filter_json.to_json, user ? user.id : nil) + snapshot_ids=extract_snapshot_ids(rows, authenticated_system) - has_security_exclusions=(snapshot_ids.size < java_result.size()) + has_security_exclusions=(snapshot_ids.size < rows.size) filter_context.process_results(snapshot_ids, has_security_exclusions) filter_context end private - def self.extract_snapshot_ids(sql_rows, authenticated_system) + def self.extract_snapshot_ids(rows, authenticated_system) sids=[] - project_ids=sql_rows.map { |r| r[2] ? to_integer(r[2]) : to_integer(r[1]) }.compact.uniq + project_ids=rows.map { |row| row.getResourceRootId() }.compact.uniq authorized_pids=authenticated_system.select_authorized(:user, project_ids) - sql_rows.each do |row| - pid=(row[2] ? to_integer(row[2]) : to_integer(row[1])) - if authorized_pids.include?(pid) - sids<", date))); - assertSnapshotIds(result, 3); - } - - @Test - public void filterOnDateIncludesTime() throws ParseException { - setupData("shared"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse("2008-12-25 03:00"); - FilterResult result = executor.execute(Filter.createForAllQualifiers().setDateCriterion(new DateCriterion("<", date))); - assertSnapshotIds(result, 2, 4); - } - - @Test - public void filterOnBaseSnapshot() { - setupData("shared"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - FilterResult result = executor.execute(Filter.createForAllQualifiers().setPath(2, 2, "")); - assertSnapshotIds(result, 4); - } - - @Test - public void sortByName() { - setupData("shared"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - FilterResult result = executor.execute(Filter.createForAllQualifiers().setSortedByName()); - assertSortedSnapshotIds(result, 2, 4, 3); - } - - @Test - public void sortByKey() { - setupData("shared"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - FilterResult result = executor.execute(Filter.createForAllQualifiers().setSortedByKey()); - assertSortedSnapshotIds(result, 3, 2, 4); - } - - @Test - public void sortByDate() { - setupData("shared"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - FilterResult result = executor.execute(Filter.createForAllQualifiers().setSortedByDate()); - assertSortedSnapshotIds(result, 2, 4, 3); - } - - @Test - public void sortByDescendingDate() { - setupData("shared"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - FilterResult result = executor.execute(Filter.createForAllQualifiers().setSortedByDate().setAscendingSort(false)); - assertSortedSnapshotIds(result, 3, 4, 2); - } - - @Test - public void sortByAscendingDate() { - setupData("shared"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - FilterResult result = executor.execute(Filter.createForAllQualifiers().setSortedByDate().setAscendingSort(true)); - assertSortedSnapshotIds(result, 2, 4, 3); - } - - @Test - public void sortByAscendingMeasureValue() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .setSortedMetricId(2, true, false); - - FilterResult result = executor.execute(filter); - assertSortedSnapshotIds(result, 6, 5); - } - - @Test - public void sortByDecendingMeasureValue() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .setSortedMetricId(2, true, false) - .setAscendingSort(false); - - FilterResult result = executor.execute(filter); - assertSortedSnapshotIds(result, 5, 6); - } - - @Test - public void applySingleMeasureCriterion() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .addMeasureCriterion(new MeasureCriterion(2, ">", 50.0, false)); - - FilterResult result = executor.execute(filter); - assertSnapshotIds(result, 5); - } - - @Test - public void applyManyMeasureCriteria() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .addMeasureCriterion(new MeasureCriterion(2, ">", 50.0, false)) - .addMeasureCriterion(new MeasureCriterion(1, ">", 100.0, false)); - - FilterResult result = executor.execute(filter); - assertSnapshotIds(result, 5); - } - - @Test - public void criteriaAreExclusive() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .addMeasureCriterion(new MeasureCriterion(2, ">", 50.0, false)) - .addMeasureCriterion(new MeasureCriterion(1, "<", 100.0, false)); - - FilterResult result = executor.execute(filter); - assertThat(result.size()).isEqualTo(0); - } - - @Test - public void sortAndFilterMeasures() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .addMeasureCriterion(new MeasureCriterion(2, ">", 5.0, false)) - .addMeasureCriterion(new MeasureCriterion(1, ">", 5.0, false)) - .setSortedMetricId(2, true, false); // sort by coverage - - FilterResult result = executor.execute(filter); - assertSnapshotIds(result, 6, 5); - } - - @Test - public void sortDescendingAndFilterMeasures() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .addMeasureCriterion(new MeasureCriterion(2, ">", 5.0, false)) // filter on coverage - .addMeasureCriterion(new MeasureCriterion(1, ">", 5.0, false)) // filter on lines - .setSortedMetricId(2, true, false) // sort by coverage - .setAscendingSort(false); - - FilterResult result = executor.execute(filter); - assertSnapshotIds(result, 5, 6); - } - - @Test - public void filterByResourceKey() { - setupData("shared"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - FilterResult result = executor.execute(Filter.createForAllQualifiers().setKeyRegexp("*:org.sonar.*")); - assertSnapshotIds(result, 4); - } - - @Test - public void filterByResourceKeyIsCaseInsensitive() { - setupData("shared"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - FilterResult result = executor.execute(Filter.createForAllQualifiers().setKeyRegexp("*:ORG.SonAR.*")); - assertSnapshotIds(result, 4); - } - - @Test - public void filterByMissingMeasureValue() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .addMeasureCriterion(new MeasureCriterion(3, ">", 0.0, false)); // filter on duplicated lines - - FilterResult result = executor.execute(filter); - assertSnapshotIds(result, 6); - } - - @Test - public void filterByMissingMeasureValues() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .addMeasureCriterion(new MeasureCriterion(1, ">", 0.0, false)) // filter on lines - .addMeasureCriterion(new MeasureCriterion(3, ">", 0.0, false)); // filter on duplicated lines - - FilterResult result = executor.execute(filter); - assertSnapshotIds(result, 6); - } - - @Test - public void sortByMissingMeasureValue() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .setSortedMetricId(3, true, false); // sort by duplicated lines - - FilterResult result = executor.execute(filter); - assertSnapshotIds(result, 5, 6); - } - - @Test - public void filterByMeasureValueAndSortOnOtherMetric() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .addMeasureCriterion(new MeasureCriterion(1, ">", 0.0, false)) // lines > 0 - .setSortedMetricId(2, true, false); // sort by coverage - - FilterResult result = executor.execute(filter); - assertSnapshotIds(result, 6, 5); - } - - @Test - public void intersectionOfCriteriaOnSameMetric() { - setupData("shared", "measures"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.CLASS)) - .addMeasureCriterion(new MeasureCriterion(1, ">", 400.0, false)) // lines > 400 - .addMeasureCriterion(new MeasureCriterion(1, "<", 600.0, false)); // lines > 400 - - FilterResult result = executor.execute(filter); - assertSnapshotIds(result, 5); - } - - @Test - public void ignoreProjectCopiesOfViews() { - setupData("views"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setQualifiers(Sets.newHashSet(Qualifiers.PROJECT)); - - FilterResult result = executor.execute(filter); - assertSnapshotIds(result, 1); // the "project copy" with id 4 is ignored - } - - @Test - public void loadProjectCopiesIfPathIsAView() { - setupData("views"); - FilterExecutor executor = new FilterExecutor(getSession(), new H2()); - Filter filter = new Filter() - .setPath(2, 2, "") - .setQualifiers(Sets.newHashSet(Qualifiers.SUBVIEW, Qualifiers.PROJECT)); - - FilterResult result = executor.execute(filter); - assertSnapshotIds(result, 3, 4); - } - - @Test - public void explicitelyUseIndexOnMsSql() { - Filter filter = new Filter().addMeasureCriterion(new MeasureCriterion(1, ">", 400.0, false)); - - String sql = new FilterExecutor(getSession(), new MsSql()).toSql(filter); - assertThat(sql).contains(" WITH (INDEX(measures_sid_metric)) "); - - sql = new FilterExecutor(getSession(), new H2()).toSql(filter); - assertThat(sql).doesNotContain(" WITH (INDEX(measures_sid_metric)) "); - } - - private void assertSnapshotIds(FilterResult result, int... snapshotIds) { - assertThat(result.size()).isEqualTo(snapshotIds.length); - for (int snapshotId : snapshotIds) { - boolean found = false; - for (Object[] row : result.getRows()) { - found |= result.getSnapshotId(row) == snapshotId; - } - if (!found) { - fail("Snapshot id not found in results: " + snapshotId); - } - } - } - - private void assertSortedSnapshotIds(FilterResult result, int... snapshotIds) { - assertThat(result.size()).isEqualTo(snapshotIds.length); - for (int index = 0; index < snapshotIds.length; index++) { - assertThat(result.getSnapshotId(result.getRows().get(index))).isEqualTo(snapshotIds[index]); - } - } -} diff --git a/sonar-server/src/test/java/org/sonar/server/filters/FilterResultTest.java b/sonar-server/src/test/java/org/sonar/server/filters/FilterResultTest.java deleted file mode 100644 index afb3565a7fe..00000000000 --- a/sonar-server/src/test/java/org/sonar/server/filters/FilterResultTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.server.filters; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsNull.nullValue; -import static org.junit.Assert.assertThat; - -public class FilterResultTest { - - @Test - public void sortWithNullElements() { - List list = Arrays.asList(new String[]{"foo"}, new String[]{null}, new String[]{"bar"}, new String[]{null}, new String[]{null}, new String[]{"toto"}); - Collections.sort(list, new FilterResult.NumericComparator(0)); - - assertThat(list.get(0)[0], nullValue()); - assertThat(list.get(1)[0], nullValue()); - assertThat(list.get(2)[0], nullValue()); - assertThat(list.get(3)[0], is("bar")); - assertThat(list.get(4)[0], is("foo")); - assertThat(list.get(5)[0], is("toto")); - } -} diff --git a/sonar-server/src/test/resources/org/sonar/server/filters/FilterExecutorTest/measures.xml b/sonar-server/src/test/resources/org/sonar/server/filters/FilterExecutorTest/measures.xml deleted file mode 100644 index 73a9937f781..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/filters/FilterExecutorTest/measures.xml +++ /dev/null @@ -1,68 +0,0 @@ - - \ No newline at end of file diff --git a/sonar-server/src/test/resources/org/sonar/server/filters/FilterExecutorTest/shared.xml b/sonar-server/src/test/resources/org/sonar/server/filters/FilterExecutorTest/shared.xml deleted file mode 100644 index cdb51032ca9..00000000000 --- a/sonar-server/src/test/resources/org/sonar/server/filters/FilterExecutorTest/shared.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file -- 2.39.5