This is the first step before the refactoring of the tables FILTERS, FILTER_COLUMNS and CRITERIAtags/3.3
@@ -81,6 +81,10 @@ | |||
<level value="WARN"/> | |||
</logger> | |||
<logger name="org.sonar.MEASURE_FILTER"> | |||
<level value="WARN"/> | |||
</logger> | |||
<root> | |||
<level value="INFO"/> | |||
<appender-ref ref="SONAR_FILE"/> |
@@ -1,5 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
@@ -64,7 +65,11 @@ | |||
<groupId>org.codehaus.plexus</groupId> | |||
<artifactId>plexus-classworlds</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.googlecode.json-simple</groupId> | |||
<artifactId>json-simple</artifactId> | |||
<version>1.1.1</version> | |||
</dependency> | |||
<!-- logging --> | |||
<dependency> |
@@ -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<String> resourceScopes = Sets.newHashSet(); | |||
private Set<String> resourceQualifiers = Sets.newHashSet(); | |||
private Set<String> resourceLanguages = Sets.newHashSet(); | |||
private boolean onBaseResourceChildren = false; // only if getBaseResourceKey is set | |||
private List<String> resourceScopes = Lists.newArrayList(); | |||
private List<String> resourceQualifiers = Lists.newArrayList(); | |||
private List<String> resourceLanguages = Lists.newArrayList(); | |||
private String resourceName; | |||
private Date fromDate = null, toDate = null; | |||
private boolean userFavourites = false; | |||
// conditions on measures | |||
private List<MeasureFilterValueCondition> measureConditions = Lists.newArrayList(); | |||
private List<MeasureFilterCondition> 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<String> resourceScopes) { | |||
this.resourceScopes = resourceScopes; | |||
public MeasureFilter setResourceScopes(@Nullable List<String> l) { | |||
this.resourceScopes = (l != null ? l : Collections.<String>emptyList()); | |||
return this; | |||
} | |||
public MeasureFilter setResourceQualifiers(String... qualifiers) { | |||
this.resourceQualifiers = Sets.newHashSet(qualifiers); | |||
public MeasureFilter setResourceQualifiers(List<String> l) { | |||
this.resourceQualifiers = (l != null ? l : Collections.<String>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<String> l) { | |||
this.resourceLanguages = (l != null ? l : Collections.<String>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<String> resourceScopes() { | |||
public List<String> getResourceScopes() { | |||
return resourceScopes; | |||
} | |||
public Set<String> resourceQualifiers() { | |||
public List<String> getResourceQualifiers() { | |||
return resourceQualifiers; | |||
} | |||
public Set<String> resourceLanguages() { | |||
public List<String> getResourceLanguages() { | |||
return resourceLanguages; | |||
} | |||
public List<MeasureFilterValueCondition> measureConditions() { | |||
public List<MeasureFilterCondition> getMeasureConditions() { | |||
return measureConditions; | |||
} | |||
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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<String>) map.get("scopes")); | |||
filter.setResourceQualifiers((List<String>) map.get("qualifiers")); | |||
filter.setResourceLanguages((List<String>) 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; | |||
} | |||
} |
@@ -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<MeasureFilterRow> execute(String filterJson, @Nullable Long userId) throws ParseException { | |||
return execute(filterJson, userId, FILTER_LOG); | |||
} | |||
@VisibleForTesting | |||
List<MeasureFilterRow> 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<MeasureFilterRow> 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<MeasureFilterRow> 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()); | |||
} | |||
} | |||
} |
@@ -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<MeasureFilterRow> execute(MeasureFilter filter, @Nullable Long userId) { | |||
public List<MeasureFilterRow> execute(MeasureFilter filter, MeasureFilterContext context) throws SQLException { | |||
List<MeasureFilterRow> 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; | |||
} | |||
} |
@@ -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"; | |||
} |
@@ -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<Date> 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(".%'"); | |||
} | |||
} | |||
} |
@@ -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]"); | |||
} | |||
} |
@@ -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<Metric>() { | |||
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(); | |||
} | |||
} |
@@ -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<MeasureFilterContext>() { | |||
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()); | |||
} | |||
} |
@@ -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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
.addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 200)); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
.addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 1000).setPeriod(5)); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
.addCondition(new MeasureFilterCondition(METRIC_LINES, ">", 2)) | |||
.addCondition(new MeasureFilterCondition(METRIC_LINES, "<=", 50)); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
public void filter_by_parent_resource() throws SQLException { | |||
MeasureFilter filter = new MeasureFilter().setBaseResourceKey("java_project").setOnBaseResourceChildren(true); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, null); | |||
List<MeasureFilterRow> 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<MeasureFilterRow> rows = executor.execute(filter, 50L); | |||
List<MeasureFilterRow> rows = executor.execute(filter, new MeasureFilterContext().setUserId(50L)); | |||
assertThat(rows).hasSize(2); | |||
verifyJavaBigFile(rows.get(0)); |
@@ -31,6 +31,10 @@ | |||
<level value="DEBUG"/> | |||
</logger> | |||
<logger name="org.sonar.MEASURE_FILTER"> | |||
<level value="DEBUG"/> | |||
</logger> | |||
<root> | |||
<level value="INFO"/> | |||
<appender-ref ref="STDOUT"/> |
@@ -31,6 +31,10 @@ | |||
<level value="DEBUG"/> | |||
</logger> | |||
<logger name="org.sonar.MEASURE_FILTER"> | |||
<level value="DEBUG"/> | |||
</logger> | |||
<root> | |||
<level value="INFO"/> | |||
<appender-ref ref="STDOUT"/> |
@@ -31,6 +31,10 @@ | |||
<level value="DEBUG"/> | |||
</logger> | |||
<logger name="org.sonar.MEASURE_FILTER"> | |||
<level value="DEBUG"/> | |||
</logger> | |||
<root> | |||
<level value="INFO"/> | |||
<appender-ref ref="STDOUT"/> |
@@ -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<String> scopes; | |||
private Set<String> qualifiers; | |||
private Set<String> languages; | |||
private Set<Integer> favouriteIds; | |||
private DateCriterion dateCriterion; | |||
private String keyRegexp; | |||
private String nameRegexp; | |||
private boolean onDirectChildren = false; | |||
// filters on measures | |||
private List<MeasureCriterion> 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<String> getScopes() { | |||
return scopes; | |||
} | |||
public boolean hasScopes() { | |||
return scopes != null && !scopes.isEmpty(); | |||
} | |||
public Filter setScopes(Set<String> scopes) { | |||
this.scopes = scopes; | |||
return this; | |||
} | |||
public Filter setScopes(String... scopes) { | |||
this.scopes = Sets.newHashSet(scopes); | |||
return this; | |||
} | |||
public Set<String> getQualifiers() { | |||
return qualifiers; | |||
} | |||
public boolean hasQualifiers() { | |||
return qualifiers != null && !qualifiers.isEmpty(); | |||
} | |||
public Filter setQualifiers(Set<String> qualifiers) { | |||
this.qualifiers = qualifiers; | |||
return this; | |||
} | |||
public Filter setQualifiers(String... qualifiers) { | |||
this.qualifiers = Sets.newHashSet(qualifiers); | |||
return this; | |||
} | |||
public Set<String> getLanguages() { | |||
return languages; | |||
} | |||
public boolean hasLanguages() { | |||
return languages != null && !languages.isEmpty(); | |||
} | |||
public Filter setLanguages(Set<String> languages) { | |||
this.languages = languages; | |||
return this; | |||
} | |||
public Filter setLanguages(String... languages) { | |||
this.languages = Sets.newHashSet(languages); | |||
return this; | |||
} | |||
public Set<Integer> getFavouriteIds() { | |||
return favouriteIds; | |||
} | |||
public boolean hasFavouriteIds() { | |||
return favouriteIds != null && !favouriteIds.isEmpty(); | |||
} | |||
public Filter setFavouriteIds(Set<Integer> 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<MeasureCriterion> getMeasureCriteria() { | |||
return measureCriteria; | |||
} | |||
public Filter setMeasureCriteria(List<MeasureCriterion> 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); | |||
} | |||
} |
@@ -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.<Object[]> 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(), '*', '%'))); | |||
} | |||
} | |||
} |
@@ -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<Object[]> rows; | |||
private Filter filter; | |||
public static final int SORTED_COLUMN_INDEX = 3; | |||
public FilterResult(Filter filter, List<Object[]> rows) { | |||
this.rows = new ArrayList<Object[]>(rows); | |||
this.filter = filter; | |||
} | |||
/** | |||
* @return a list of arrays | |||
*/ | |||
public List<Object[]> 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<Object[]> 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<Object[]> 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<Object[]>, 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<Object[]>, 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); | |||
} | |||
} | |||
} | |||
@@ -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(); | |||
} | |||
} |
@@ -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); |
@@ -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<MeasureFilterRow> executeMeasureFilter(String json, @Nullable Long userId) throws Exception { | |||
return get(MeasureFilterEngine.class).execute(json, userId); | |||
} | |||
public Collection<ResourceType> 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()); | |||
} | |||
} | |||
} |
@@ -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 |
@@ -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<<to_integer(row[0]) | |||
filter_rows.each do |row| | |||
if authorized_pids.include?(row.getResourceRootId()) | |||
sids<<row.getSnapshotId() | |||
end | |||
end | |||
sids | |||
end | |||
def to_integer(obj) | |||
if obj.is_a?(Fixnum) | |||
obj | |||
else | |||
# java.math.BigDecimal | |||
obj.intValue() | |||
end | |||
end | |||
end |
@@ -21,82 +21,71 @@ class Filters | |||
def self.execute(filter, authenticated_system, options={}) | |||
filter_context = FilterContext.new(filter, options) | |||
java_filter=Java::OrgSonarServerFilters::Filter.new | |||
filter_json={} | |||
#----- FILTER ON RESOURCES | |||
if filter.resource_id | |||
snapshot=Snapshot.find(:first, :conditions => {: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<<to_integer(row[0]) | |||
rows.each do |row| | |||
if authorized_pids.include?(row.getResourceRootId()) | |||
sids<<row.getSnapshotId() | |||
end | |||
end | |||
sids | |||
end | |||
def self.to_integer(obj) | |||
if obj.is_a?(Fixnum) | |||
obj | |||
else | |||
# java.math.BigDecimal | |||
obj.intValue() | |||
end | |||
end | |||
end |
@@ -1,47 +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.time.DateUtils; | |||
import org.junit.Test; | |||
import java.util.Date; | |||
import static org.hamcrest.CoreMatchers.is; | |||
import static org.junit.Assert.assertThat; | |||
public class DateCriterionTest { | |||
@Test | |||
public void ignoreTime() { | |||
DateCriterion criterion = new DateCriterion().setDate(3); | |||
Date date = criterion.getDate(); | |||
assertThat(date.getHours(), is(0)); | |||
assertThat(date.getMinutes(), is(0)); | |||
} | |||
@Test | |||
public void testDaysAgo() { | |||
DateCriterion criterion = new DateCriterion().setDate(3); | |||
Date date = criterion.getDate(); | |||
assertThat(date.getMinutes(), is(0)); | |||
assertThat(date.getHours(), is(0)); | |||
assertThat(DateUtils.isSameDay(date, DateUtils.addDays(new Date(), -3)), is(true)); | |||
} | |||
} |
@@ -1,361 +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.Sets; | |||
import org.junit.Test; | |||
import org.sonar.api.resources.Qualifiers; | |||
import org.sonar.api.resources.Resource; | |||
import org.sonar.core.persistence.dialect.H2; | |||
import org.sonar.core.persistence.dialect.MsSql; | |||
import org.sonar.jpa.test.AbstractDbUnitTestCase; | |||
import java.text.ParseException; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Date; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.fest.assertions.Fail.fail; | |||
public class FilterExecutorTest extends AbstractDbUnitTestCase { | |||
@Test | |||
public void mustDefineAtLeastOneQualifier() { | |||
setupData("shared"); | |||
FilterExecutor executor = new FilterExecutor(getSession(), new H2()); | |||
FilterResult result = executor.execute(new Filter()); | |||
assertThat(result.size()).isEqualTo(0);// no qualifiers | |||
} | |||
@Test | |||
public void filterOnScopes() { | |||
setupData("shared"); | |||
FilterExecutor executor = new FilterExecutor(getSession(), new H2()); | |||
FilterResult result = executor.execute(Filter.createForAllQualifiers().setScopes(Sets.newHashSet(Resource.SCOPE_SPACE))); | |||
assertSnapshotIds(result, 4); | |||
} | |||
@Test | |||
public void filterOnQualifiers() { | |||
setupData("shared"); | |||
FilterExecutor executor = new FilterExecutor(getSession(), new H2()); | |||
FilterResult result = executor.execute(new Filter().setQualifiers(Sets.newHashSet(Resource.QUALIFIER_PROJECT, Resource.QUALIFIER_MODULE))); | |||
assertSnapshotIds(result, 2, 3); | |||
} | |||
@Test | |||
public void filterOnLanguages() { | |||
setupData("shared"); | |||
FilterExecutor executor = new FilterExecutor(getSession(), new H2()); | |||
FilterResult result = executor.execute(Filter.createForAllQualifiers().setLanguages(Sets.newHashSet("java"))); | |||
assertSnapshotIds(result, 2, 4); | |||
} | |||
@Test | |||
public void filterOnDate() throws ParseException { | |||
setupData("shared"); | |||
FilterExecutor executor = new FilterExecutor(getSession(), new H2()); | |||
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse("2008-12-26 00:00"); | |||
FilterResult result = executor.execute(Filter.createForAllQualifiers().setDateCriterion(new DateCriterion(">", 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]); | |||
} | |||
} | |||
} |
@@ -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<String[]> 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")); | |||
} | |||
} |
@@ -1,68 +0,0 @@ | |||
<dataset> | |||
<metrics delete_historical_data="[null]" id="1" name="lines" val_type="FLOAT" description="Lines" domain="Size" | |||
short_name="Lines" qualitative="false" user_managed="false" enabled="true" origin="JAV" worst_value="[null]" | |||
optimized_best_value="[null]" best_value="[null]" direction="1" hidden="false"/> | |||
<metrics delete_historical_data="[null]" id="2" name="coverage" val_type="PERCENT" description="Coverage" domain="Tests" | |||
short_name="Coverage" qualitative="true" user_managed="false" enabled="true" origin="JAV" worst_value="[null]" | |||
optimized_best_value="[null]" best_value="[null]" direction="1" hidden="false"/> | |||
<metrics delete_historical_data="[null]" id="3" name="duplicated_lines" val_type="INT" description="Duplicated lines" domain="Duplications" | |||
short_name="Duplications" qualitative="false" user_managed="false" enabled="true" origin="JAV" worst_value="[null]" | |||
optimized_best_value="[null]" best_value="[null]" direction="1" hidden="false"/> | |||
<!-- Java classes --> | |||
<projects long_name="org.sonar.foo:File1" id="4" scope="FIL" kee="project:java:org.sonar.foo:File1" qualifier="CLA" name="File1" | |||
root_id="1" | |||
description="[null]" enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
<projects long_name="org.sonar.foo:File2" id="5" scope="FIL" kee="project:java:org.sonar.foo:File2" qualifier="CLA" name="File2" | |||
root_id="1" | |||
description="[null]" enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
<snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]" id="5" created_at="2008-12-25 01:00:01.00" build_date="2008-12-25 01:00:01.00" version="1.0" project_id="4" scope="FIL" qualifier="CLA" | |||
root_project_id="1" root_snapshot_id="2" parent_snapshot_id="4" STATUS="P" ISLAST="true" | |||
path="2.4." | |||
depth="2"/> | |||
<snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]" id="6" created_at="2008-12-25 01:00:01.00" build_date="2008-12-25 01:00:01.00" version="1.0" project_id="5" scope="FIL" qualifier="CLA" | |||
root_project_id="1" root_snapshot_id="2" parent_snapshot_id="4" STATUS="P" ISLAST="true" | |||
path="2.4." | |||
depth="2"/> | |||
<!-- SNAPSHOT 5 : 500 lines and coverage 80.5% --> | |||
<project_measures characteristic_id="[null]" id="1" metric_id="1" value="500" snapshot_id="5" | |||
url="[null]" variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]" | |||
rule_priority="[null]" alert_text="[null]" RULES_CATEGORY_ID="[null]" | |||
RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]" | |||
alert_status="[null]" description="[null]"/> | |||
<project_measures characteristic_id="[null]" id="2" metric_id="2" value="80.5" snapshot_id="5" | |||
url="[null]" variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]" | |||
rule_priority="[null]" alert_text="[null]" RULES_CATEGORY_ID="[null]" | |||
RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]" | |||
alert_status="[null]" description="[null]"/> | |||
<!-- SNAPSHOT 6 : 30 lines, coverage 20.6% and 10 duplicated lines --> | |||
<project_measures characteristic_id="[null]" id="3" metric_id="1" value="30" snapshot_id="6" | |||
url="[null]" variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]" | |||
rule_priority="[null]" alert_text="[null]" RULES_CATEGORY_ID="[null]" | |||
RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]" | |||
alert_status="[null]" description="[null]"/> | |||
<project_measures characteristic_id="[null]" id="4" metric_id="2" value="20.6" snapshot_id="6" | |||
url="[null]" variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]" | |||
rule_priority="[null]" alert_text="[null]" RULES_CATEGORY_ID="[null]" | |||
RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]" | |||
alert_status="[null]" description="[null]"/> | |||
<project_measures characteristic_id="[null]" id="5" metric_id="3" value="10" snapshot_id="6" | |||
url="[null]" variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]" | |||
rule_priority="[null]" alert_text="[null]" RULES_CATEGORY_ID="[null]" | |||
RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]" | |||
alert_status="[null]" description="[null]"/> | |||
</dataset> |
@@ -1,39 +0,0 @@ | |||
<dataset> | |||
<projects long_name="java project" id="1" scope="PRJ" kee="project:java" qualifier="TRK" name="java project" | |||
root_id="[null]" | |||
description="[null]" enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
<projects long_name="php project" id="2" scope="PRJ" kee="project:a-php-project" qualifier="TRK" name="php project" | |||
root_id="[null]" | |||
description="[null]" enabled="true" language="php" copy_resource_id="[null]" person_id="[null]"/> | |||
<projects long_name="org.sonar.foo" id="3" scope="DIR" kee="project:java:org.sonar.foo" qualifier="PAC" name="org.sonar.foo" | |||
root_id="1" | |||
description="[null]" enabled="true" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
<!-- Java project --> | |||
<snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]" id="1" created_at="2008-12-20 00:00:00.00" build_date="2008-12-20 00:00:00.00" version="1.0" project_id="1" scope="PRJ" qualifier="TRK" | |||
root_project_id="1" root_snapshot_id="[null]" parent_snapshot_id="[null]" STATUS="P" ISLAST="false" | |||
path="" | |||
depth="0"/> | |||
<snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]" id="2" created_at="2008-12-25 01:00:00.00" build_date="2008-12-25 01:00:00.00" version="1.0" project_id="1" scope="PRJ" qualifier="TRK" | |||
root_project_id="1" root_snapshot_id="[null]" parent_snapshot_id="[null]" STATUS="P" ISLAST="true" | |||
path="" | |||
depth="0"/> | |||
<!-- PHP project --> | |||
<snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]" id="3" created_at="2008-12-31 02:00:00.00" build_date="2008-12-31 02:00:00.00" version="1.0" project_id="2" scope="PRJ" qualifier="TRK" | |||
root_project_id="2" root_snapshot_id="[null]" parent_snapshot_id="[null]" STATUS="P" ISLAST="true" | |||
path="" | |||
depth="0"/> | |||
<!-- Java package --> | |||
<snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]" id="4" created_at="2008-12-25 01:00:01.00" build_date="2008-12-25 01:00:01.00" version="1.0" project_id="3" scope="DIR" qualifier="PAC" | |||
root_project_id="1" root_snapshot_id="2" parent_snapshot_id="2" STATUS="P" ISLAST="true" | |||
path="2." | |||
depth="1"/> | |||
</dataset> |