From 0d04b2f74fd9ab532b3dc82f4ebeebac8b2cca4f Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Mon, 9 May 2016 16:13:08 +0200 Subject: [PATCH] SONAR-7576 WS api/measures/component_tree filter components without measure on the sorted metric --- .../measure/ws/ComponentTreeAction.java | 19 +++++++ .../measure/ws/ComponentTreeDataLoader.java | 54 ++++++++++++++++++- .../measure/ws/ComponentTreeActionTest.java | 50 +++++++++++++++-- .../measure/ComponentTreeWsRequest.java | 11 ++++ .../ws/client/measure/MeasuresService.java | 4 +- .../client/measure/MeasuresWsParameters.java | 1 + .../client/measure/MeasuresServiceTest.java | 6 ++- 7 files changed, 138 insertions(+), 7 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java index 8af4ffe482b..be23ef35f58 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java @@ -58,6 +58,7 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOP import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_PERIOD_SORT; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT; +import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT_FILTER; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_QUALIFIERS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_STRATEGY; @@ -80,16 +81,21 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_STRATEG public class ComponentTreeAction implements MeasuresWsAction { private static final int MAX_SIZE = 500; private static final int QUERY_MINIMUM_LENGTH = 3; + // tree exploration strategies static final String ALL_STRATEGY = "all"; static final String CHILDREN_STRATEGY = "children"; static final String LEAVES_STRATEGY = "leaves"; static final Set STRATEGIES = ImmutableSortedSet.of(ALL_STRATEGY, CHILDREN_STRATEGY, LEAVES_STRATEGY); + // sort static final String NAME_SORT = "name"; static final String PATH_SORT = "path"; static final String QUALIFIER_SORT = "qualifier"; static final String METRIC_SORT = "metric"; static final String METRIC_PERIOD_SORT = "metricPeriod"; static final Set SORTS = ImmutableSortedSet.of(NAME_SORT, PATH_SORT, QUALIFIER_SORT, METRIC_SORT, METRIC_PERIOD_SORT); + static final String ALL_METRIC_SORT_FILTER = "all"; + static final String WITH_MEASURES_ONLY_METRIC_SORT_FILTER = "withMeasuresOnly"; + static final Set METRIC_SORT_FILTERS = ImmutableSortedSet.of(ALL_METRIC_SORT_FILTER, WITH_MEASURES_ONLY_METRIC_SORT_FILTER); private final ComponentTreeDataLoader dataLoader; private final UserSession userSession; @@ -152,6 +158,15 @@ public class ComponentTreeAction implements MeasuresWsAction { .setSince("5.5") .setPossibleValues(1, 2, 3, 4, 5); + action.createParam(PARAM_METRIC_SORT_FILTER) + .setDescription(format("Filter components. Sort must be on a metric. Possible values are: " + + "
    " + + "
  • %s: return all components
  • " + + "
  • %s: filter out components that do not have a measure on the sorted metric
  • " + + "
", ALL_METRIC_SORT_FILTER, WITH_MEASURES_ONLY_METRIC_SORT_FILTER)) + .setDefaultValue(ALL_METRIC_SORT_FILTER) + .setPossibleValues(METRIC_SORT_FILTERS); + createMetricKeysParameter(action); createAdditionalFieldsParameter(action); createDeveloperParameters(action); @@ -253,6 +268,7 @@ public class ComponentTreeAction implements MeasuresWsAction { .setSort(request.paramAsStrings(Param.SORT)) .setAsc(request.paramAsBoolean(Param.ASCENDING)) .setMetricSort(request.param(PARAM_METRIC_SORT)) + .setMetricSortFilter(request.mandatoryParam(PARAM_METRIC_SORT_FILTER)) .setMetricPeriodSort(request.paramAsInt(PARAM_METRIC_PERIOD_SORT)) .setDeveloperId(request.param(PARAM_DEVELOPER_ID)) .setDeveloperKey(request.param(PARAM_DEVELOPER_KEY)) @@ -273,6 +289,9 @@ public class ComponentTreeAction implements MeasuresWsAction { "To sort by the '%s' metric, it must be in the list of metric keys in the '%s' parameter", metricSortValue, PARAM_METRIC_KEYS); checkRequest(componentTreeWsRequest.getMetricPeriodSort() == null ^ componentTreeWsRequest.getSort().contains(METRIC_PERIOD_SORT), "To sort by a metric period, the '%s' parameter must contain '%s' and the '%s' must be provided.", Param.SORT, METRIC_PERIOD_SORT, PARAM_METRIC_PERIOD_SORT); + checkRequest(ALL_METRIC_SORT_FILTER.equals(componentTreeWsRequest.getMetricSortFilter()) || metricSortValue != null, + "To filter components based on the sort metric, the '%s' parameter must contain '%s' or '%s' and the '%s' parameter must be provided", + Param.SORT, METRIC_SORT, METRIC_PERIOD_SORT, PARAM_METRIC_SORT); return componentTreeWsRequest; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java index 789a2171d00..aa82944b8b6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java @@ -21,6 +21,7 @@ package org.sonar.server.measure.ws; import com.google.common.base.Function; import com.google.common.base.Joiner; +import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.HashBasedTable; @@ -61,6 +62,7 @@ import org.sonarqube.ws.WsMeasures; import org.sonarqube.ws.client.measure.ComponentTreeWsRequest; import static com.google.common.base.Objects.firstNonNull; +import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.FluentIterable.from; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Sets.newHashSet; @@ -76,6 +78,7 @@ import static org.sonar.server.measure.ws.ComponentTreeAction.LEAVES_STRATEGY; import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_PERIOD_SORT; import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_SORT; import static org.sonar.server.measure.ws.ComponentTreeAction.NAME_SORT; +import static org.sonar.server.measure.ws.ComponentTreeAction.WITH_MEASURES_ONLY_METRIC_SORT_FILTER; import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods; import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException; @@ -116,6 +119,7 @@ public class ComponentTreeDataLoader { Table measuresByComponentUuidAndMetric = searchMeasuresByComponentUuidAndMetric(dbSession, baseComponent, baseSnapshot, components, metrics, periods, developerId); + components = filterComponents(components, measuresByComponentUuidAndMetric, metrics, wsRequest); components = sortComponents(components, wsRequest, metrics, measuresByComponentUuidAndMetric); components = paginateComponents(components, componentCount, wsRequest); Map referenceComponentsById = searchReferenceComponentsById(dbSession, components); @@ -227,8 +231,8 @@ public class ComponentTreeDataLoader { /** * Conditions for best value measure: *
    - *
  • component is a production file or test file
  • - *
  • metric is optimized for best value
  • + *
  • component is a production file or test file
  • + *
  • metric is optimized for best value
  • *
*/ private static void addBestValuesToMeasures(Table measuresByComponentUuidAndMetric, List components, @@ -251,6 +255,22 @@ public class ComponentTreeDataLoader { } } + private static List filterComponents(List components, + Table measuresByComponentUuidAndMetric, + List metrics, ComponentTreeWsRequest wsRequest) { + if (!WITH_MEASURES_ONLY_METRIC_SORT_FILTER.equals(wsRequest.getMetricSortFilter())) { + return components; + } + + final String metricKeyToSort = wsRequest.getMetricSort(); + Optional metricToSort = from(metrics).firstMatch(new MatchMetricKey(metricKeyToSort)); + checkState(metricToSort.isPresent(), "Metric '%s' not found", metricKeyToSort, wsRequest.getMetricKeys()); + + return from(components) + .filter(new HasMeasure(measuresByComponentUuidAndMetric, metricToSort.get())) + .toList(); + } + private static List sortComponents(List components, ComponentTreeWsRequest wsRequest, List metrics, Table measuresByComponentUuidAndMetric) { if (!isSortByMetric(wsRequest)) { @@ -276,6 +296,7 @@ public class ComponentTreeDataLoader { } private static boolean isSortByMetric(ComponentTreeWsRequest wsRequest) { + requireNonNull(wsRequest.getSort()); return wsRequest.getSort().contains(METRIC_SORT) || wsRequest.getSort().contains(METRIC_PERIOD_SORT); } @@ -389,9 +410,38 @@ public class ComponentTreeDataLoader { private enum ComponentDtoWithSnapshotIdToCopyResourceIdFunction implements Function { INSTANCE; + @Override public Long apply(@Nonnull ComponentDtoWithSnapshotId input) { return input.getCopyResourceId(); } } + + private static class HasMeasure implements Predicate { + private final Table measuresByComponentUuidAndMetric; + private final MetricDto metric; + + private HasMeasure(Table measuresByComponentUuidAndMetric, MetricDto metric) { + this.measuresByComponentUuidAndMetric = measuresByComponentUuidAndMetric; + this.metric = metric; + } + + @Override + public boolean apply(@Nonnull ComponentDtoWithSnapshotId input) { + return measuresByComponentUuidAndMetric.contains(input.uuid(), metric); + } + } + + private static class MatchMetricKey implements Predicate { + private final String metricKeyToSort; + + private MatchMetricKey(String metricKeyToSort) { + this.metricKeyToSort = metricKeyToSort; + } + + @Override + public boolean apply(@Nonnull MetricDto input) { + return input.getKey().equals(metricKeyToSort); + } + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java index cc932d405b9..f523642673f 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java @@ -70,6 +70,7 @@ import static org.sonar.server.measure.ws.ComponentTreeAction.LEAVES_STRATEGY; import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_PERIOD_SORT; import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_SORT; import static org.sonar.server.measure.ws.ComponentTreeAction.NAME_SORT; +import static org.sonar.server.measure.ws.ComponentTreeAction.WITH_MEASURES_ONLY_METRIC_SORT_FILTER; import static org.sonar.test.JsonAssert.assertJson; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_PERIODS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS; @@ -79,6 +80,7 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOP import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_PERIOD_SORT; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT; +import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT_FILTER; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_QUALIFIERS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_STRATEGY; @@ -256,6 +258,7 @@ public class ComponentTreeActionTest { public void sort_by_metric_value() { ComponentDto projectDto = newProjectDto("project-uuid"); SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(projectDto); + componentDb.insertComponentAndSnapshot(newFileDto(projectDto, "file-uuid-4"), projectSnapshot); SnapshotDto fileSnapshot3 = componentDb.insertComponentAndSnapshot(newFileDto(projectDto, "file-uuid-3"), projectSnapshot); SnapshotDto fileSnapshot1 = componentDb.insertComponentAndSnapshot(newFileDto(projectDto, "file-uuid-1"), projectSnapshot); SnapshotDto fileSnapshot2 = componentDb.insertComponentAndSnapshot(newFileDto(projectDto, "file-uuid-2"), projectSnapshot); @@ -273,7 +276,35 @@ public class ComponentTreeActionTest { .setParam(PARAM_METRIC_SORT, "ncloc") .setParam(PARAM_METRIC_KEYS, "ncloc")); - assertThat(response.getComponentsList()).extracting("id").containsExactly("file-uuid-1", "file-uuid-2", "file-uuid-3"); + assertThat(response.getComponentsList()).extracting("id").containsExactly("file-uuid-1", "file-uuid-2", "file-uuid-3", "file-uuid-4"); + } + + @Test + public void remove_components_without_measure_on_the_metric_sort() { + ComponentDto projectDto = newProjectDto("project-uuid"); + SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(projectDto); + componentDb.insertComponentAndSnapshot(newFileDto(projectDto, "file-uuid-4"), projectSnapshot); + SnapshotDto fileSnapshot3 = componentDb.insertComponentAndSnapshot(newFileDto(projectDto, "file-uuid-3"), projectSnapshot); + SnapshotDto fileSnapshot1 = componentDb.insertComponentAndSnapshot(newFileDto(projectDto, "file-uuid-1"), projectSnapshot); + SnapshotDto fileSnapshot2 = componentDb.insertComponentAndSnapshot(newFileDto(projectDto, "file-uuid-2"), projectSnapshot); + MetricDto ncloc = newMetricDtoWithoutOptimization().setKey("ncloc").setValueType(ValueType.INT.name()).setDirection(1); + dbClient.metricDao().insert(dbSession, ncloc); + dbClient.measureDao().insert(dbSession, + newMeasureDto(ncloc, fileSnapshot1.getId()).setValue(1.0d), + newMeasureDto(ncloc, fileSnapshot2.getId()).setValue(2.0d), + newMeasureDto(ncloc, fileSnapshot3.getId()).setValue(3.0d)); + db.commit(); + + ComponentTreeWsResponse response = call(ws.newRequest() + .setParam(PARAM_BASE_COMPONENT_ID, "project-uuid") + .setParam(Param.SORT, METRIC_SORT) + .setParam(PARAM_METRIC_SORT, "ncloc") + .setParam(PARAM_METRIC_KEYS, "ncloc") + .setParam(PARAM_METRIC_SORT_FILTER, WITH_MEASURES_ONLY_METRIC_SORT_FILTER)); + + assertThat(response.getComponentsList()).extracting("id") + .containsExactly("file-uuid-1", "file-uuid-2", "file-uuid-3") + .doesNotContain("file-uuid-4"); } @Test @@ -393,8 +424,7 @@ public class ComponentTreeActionTest { ComponentTreeWsResponse result = call(ws.newRequest() .setParam(PARAM_BASE_COMPONENT_ID, projectUuid) .setParam(PARAM_STRATEGY, LEAVES_STRATEGY) - .setParam(PARAM_METRIC_KEYS, "ncloc") - ); + .setParam(PARAM_METRIC_KEYS, "ncloc")); assertThat(result.getBaseComponent().getId()).isEqualTo(projectUuid); assertThat(result.getComponentsCount()).isEqualTo(0); @@ -527,6 +557,20 @@ public class ComponentTreeActionTest { .setParam(Param.PAGE_SIZE, "2540")); } + @Test + public void fail_when_with_measures_only_and_no_metric_sort() { + componentDb.insertProjectAndSnapshot(newProjectDto("project-uuid")); + insertNclocMetric(); + expectedException.expect(BadRequestException.class); + expectedException + .expectMessage("To filter components based on the sort metric, the 's' parameter must contain 'metric' or 'metricPeriod' and the 'metricSort' parameter must be provided"); + + call(ws.newRequest() + .setParam(PARAM_BASE_COMPONENT_ID, "project-uuid") + .setParam(PARAM_METRIC_KEYS, "ncloc") + .setParam(PARAM_METRIC_SORT_FILTER, WITH_MEASURES_ONLY_METRIC_SORT_FILTER)); + } + private static ComponentTreeWsResponse call(TestRequest request) { TestResponse testResponse = request .setMediaType(MediaTypes.PROTOBUF) diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentTreeWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentTreeWsRequest.java index 86727a6c5c3..1d7322a486b 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentTreeWsRequest.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentTreeWsRequest.java @@ -35,6 +35,7 @@ public class ComponentTreeWsRequest { private Boolean asc; private String metricSort; private Integer metricPeriodSort; + private String metricSortFilter; private List metricKeys; private Integer page; private Integer pageSize; @@ -121,6 +122,16 @@ public class ComponentTreeWsRequest { return this; } + @CheckForNull + public String getMetricSortFilter() { + return metricSortFilter; + } + + public ComponentTreeWsRequest setMetricSortFilter(@Nullable String metricSortFilter) { + this.metricSortFilter = metricSortFilter; + return this; + } + @CheckForNull public List getMetricKeys() { return metricKeys; diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresService.java index 6eab2608301..c6c39285761 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresService.java @@ -37,6 +37,7 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOP import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOPER_KEY; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT; +import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT_FILTER; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_QUALIFIERS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_STRATEGY; @@ -60,7 +61,8 @@ public class MeasuresService extends BaseService { .setParam("ps", request.getPageSize()) .setParam("s", inlineMultipleParamValue(request.getSort())) .setParam("asc", request.getAsc()) - .setParam(PARAM_METRIC_SORT, request.getMetricSort()); + .setParam(PARAM_METRIC_SORT, request.getMetricSort()) + .setParam(PARAM_METRIC_SORT_FILTER, request.getMetricSortFilter()); return call(getRequest, ComponentTreeWsResponse.parser()); } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresWsParameters.java index b06b762c8e8..d884f76cbd1 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresWsParameters.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresWsParameters.java @@ -39,6 +39,7 @@ public class MeasuresWsParameters { public static final String PARAM_METRIC_KEYS = "metricKeys"; public static final String PARAM_METRIC_SORT = "metricSort"; public static final String PARAM_METRIC_PERIOD_SORT = "metricPeriodSort"; + public static final String PARAM_METRIC_SORT_FILTER = "metricSortFilter"; public static final String PARAM_ADDITIONAL_FIELDS = "additionalFields"; public static final String PARAM_COMPONENT_ID = "componentId"; public static final String PARAM_COMPONENT_KEY = "componentKey"; diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/MeasuresServiceTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/MeasuresServiceTest.java index 869dd6fcd21..d5087a971c1 100644 --- a/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/MeasuresServiceTest.java +++ b/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/MeasuresServiceTest.java @@ -38,6 +38,7 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOP import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOPER_KEY; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT; +import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT_FILTER; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_QUALIFIERS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_STRATEGY; @@ -51,6 +52,7 @@ public class MeasuresServiceTest { private static final List VALUE_SORT = newArrayList("qualifier", "metric"); private static final boolean VALUE_ASC = false; private static final String VALUE_METRIC_SORT = "ncloc"; + private static final String VALUE_METRIC_SORT_FILTER = "all"; private static final int VALUE_PAGE = 42; private static final int VALUE_PAGE_SIZE = 1984; private static final String VALUE_QUERY = "query-sq"; @@ -78,7 +80,8 @@ public class MeasuresServiceTest { .setPageSize(VALUE_PAGE_SIZE) .setQuery(VALUE_QUERY) .setDeveloperId(VALUE_DEVELOPER_ID) - .setDeveloperKey(VALUE_DEVELOPER_KEY); + .setDeveloperKey(VALUE_DEVELOPER_KEY) + .setMetricSortFilter(VALUE_METRIC_SORT_FILTER); underTest.componentTree(componentTreeRequest); GetRequest getRequest = serviceTester.getGetRequest(); @@ -99,6 +102,7 @@ public class MeasuresServiceTest { .hasParam("q", VALUE_QUERY) .hasParam(PARAM_DEVELOPER_ID, VALUE_DEVELOPER_ID) .hasParam(PARAM_DEVELOPER_KEY, VALUE_DEVELOPER_KEY) + .hasParam(PARAM_METRIC_SORT_FILTER, VALUE_METRIC_SORT_FILTER) .andNoOtherParam(); } } -- 2.39.5