Skip to content

Commit

Permalink
[4352] Add support to range column filters in table
Browse files Browse the repository at this point in the history
Bug: #4352
Signed-off-by: Florian ROUËNÉ <[email protected]>
  • Loading branch information
frouene committed Dec 31, 2024
1 parent 4b778e9 commit 55cb3ac
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 18 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

=== Improvements


- https://github.com/eclipse-sirius/sirius-web/issues/4352[#4352] [table] Add support to range column filters in table

== v2025.1.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -56,10 +55,30 @@ public Map<String, List<String>> getParameterValues(String kind) {
@Override
public List<String> getParameterEntries(String value) {
if (value.startsWith("[") && value.endsWith("]") && value.length() >= 3) {
var rawValue = value.substring(1);
rawValue = rawValue.substring(0, rawValue.indexOf("]"));
return Arrays.stream(rawValue.split(",")).toList();
var rawValue = value.substring(1, value.length() - 1);
return this.parseEntries(rawValue);
}
return List.of();
}

private List<String> parseEntries(String rawValue) {
List<String> entries = new ArrayList<>();
int start = 0;
int level = 0;

for (int i = 0; i < rawValue.length(); i++) {
char c = rawValue.charAt(i);
if (c == '[') {
level++;
} else if (c == ']') {
level--;
} else if (c == ',' && level == 0) {
entries.add(rawValue.substring(start, i).trim());
start = i + 1;
}
}
entries.add(rawValue.substring(start).trim());

return entries;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*******************************************************************************
* Copyright (c) 2024 CEA LIST.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.core;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import org.junit.jupiter.api.Test;

/**
* Tests of the URLParser.
*
* @author frouene
*/
public class URLParserTests {

@Test
public void testGetParameterEntriesWithNestedLists() {
URLParser parser = new URLParser();
String value = "[467ec1c3-8ba7-32dc-9d72-71a2ccad161b:[\"1\",\"\"],4752c1c3-8ba7-32dc-9d72-71a2ccad161b:[\"1\",\"2\"]]";
List<String> expected = List.of(
"467ec1c3-8ba7-32dc-9d72-71a2ccad161b:[\"1\",\"\"]",
"4752c1c3-8ba7-32dc-9d72-71a2ccad161b:[\"1\",\"2\"]"
);
List<String> result = parser.getParameterEntries(value);
assertThat(expected).isEqualTo(result);
}

@Test
public void testGetParameterEntriesWithEmptyList() {
URLParser parser = new URLParser();
String value = "[]";
List<String> expected = List.of();
List<String> result = parser.getParameterEntries(value);
assertThat(expected).isEqualTo(result);
}

@Test
public void testGetParameterEntriesWithSingleElement() {
URLParser parser = new URLParser();
String value = "[467ec1c3-8ba7-32dc-9d72-71a2ccad161b:[\"1\",\"\"]]";
List<String> expected = List.of("467ec1c3-8ba7-32dc-9d72-71a2ccad161b:[\"1\",\"\"]");
List<String> result = parser.getParameterEntries(value);
assertThat(expected).isEqualTo(result);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@
import org.eclipse.sirius.components.core.api.IEditingContextRepresentationDescriptionProvider;
import org.eclipse.sirius.components.core.api.IIdentityService;
import org.eclipse.sirius.components.core.api.ILabelService;
import org.eclipse.sirius.components.papaya.AnnotableElement;
import org.eclipse.sirius.components.papaya.PapayaFactory;
import org.eclipse.sirius.components.papaya.PapayaPackage;
import org.eclipse.sirius.components.papaya.Type;
import org.eclipse.sirius.components.papaya.spec.PackageSpec;
import org.eclipse.sirius.components.representations.IRepresentationDescription;
import org.eclipse.sirius.components.representations.VariableManager;
import org.eclipse.sirius.components.tables.ColumnFilter;
import org.eclipse.sirius.components.tables.ColumnFilterMapped;
import org.eclipse.sirius.components.tables.descriptions.ColumnDescription;
import org.eclipse.sirius.components.tables.descriptions.ICellDescription;
import org.eclipse.sirius.components.tables.descriptions.IconLabelCellDescription;
Expand Down Expand Up @@ -124,7 +125,7 @@ private PaginatedData getSemanticElements(VariableManager variableManager) {
var direction = variableManager.get(TableRenderer.PAGINATION_DIRECTION, String.class).orElse(null);
var size = variableManager.get(TableRenderer.PAGINATION_SIZE, Integer.class).orElse(0);
var globalFilter = variableManager.get(TableRenderer.GLOBAL_FILTER_DATA, String.class).orElse(null);
List<ColumnFilter> columnFilters = variableManager.get(TableRenderer.COLUMN_FILTERS, List.class).orElse(List.of());
List<ColumnFilterMapped> columnFilters = variableManager.get(TableRenderer.COLUMN_FILTERS, List.class).orElse(List.of());

Predicate<EObject> predicate = eObject -> {
boolean isValidCandidate = eObject instanceof Type && EcoreUtil.isAncestor(self, eObject);
Expand All @@ -137,10 +138,28 @@ private PaginatedData getSemanticElements(VariableManager variableManager) {
isValidCandidate = isValidCandidate || type.getAnnotations().stream().anyMatch(annotation -> annotation.getName().contains(globalFilter));
}
isValidCandidate = isValidCandidate && columnFilters.stream().allMatch(columnFilter -> {
if (columnFilter.id().equals("papaya.NamedElement#name")) {
return type.getName() != null && type.getName().contains(columnFilter.value());
boolean isValidColumFilterCandidat = true;
if (columnFilter.id().equals("papaya.NamedElement#name") && !columnFilter.values().isEmpty()) {
isValidColumFilterCandidat = type.getName() != null && type.getName().contains(columnFilter.values().get(0));
} else if (columnFilter.id().equals("papaya.Type#nbAnnotation") && columnFilter.values().size() == 2) {
int nbAnnotation = type.getAnnotations().size();
if (columnFilter.values().get(0) != null && !columnFilter.values().get(0).isBlank()) {
try {
int minValue = Integer.parseInt(columnFilter.values().get(0));
isValidColumFilterCandidat = minValue <= nbAnnotation;
} catch (NumberFormatException ignored) {
}
}
if (columnFilter.values().get(1) != null && !columnFilter.values().get(1).isBlank()) {
try {
int maxValue = Integer.parseInt(columnFilter.values().get(1));
isValidColumFilterCandidat = isValidColumFilterCandidat && maxValue >= nbAnnotation;
} catch (NumberFormatException e) {
isValidColumFilterCandidat = true;
}
}
}
return true;
return isValidColumFilterCandidat;

});
}
Expand All @@ -166,6 +185,18 @@ private List<ColumnDescription> getColumnDescriptions() {
.filterVariantProvider(variableManager -> "text")
.build();

ColumnDescription nbAnnotationsColumn = ColumnDescription.newColumnDescription(UUID.nameUUIDFromBytes("nbAnnotation".getBytes()).toString())
.semanticElementsProvider(variableManager -> List.of("NbAnnotationColumn"))
.headerLabelProvider(variableManager -> "Annotation number")
.headerIconURLsProvider(variableManager -> List.of("/icons/svg/Default.svg"))
.headerIndexLabelProvider(variableManager -> "")
.targetObjectIdProvider(variableManager -> "papaya.Type#nbAnnotation")
.targetObjectKindProvider(variableManager -> "")
.initialWidthProvider(variableManager -> 250)
.isResizablePredicate(variableManager -> false)
.filterVariantProvider(variableManager -> "range")
.build();

Function<VariableManager, String> headerLabelProvider = variableManager -> variableManager.get(VariableManager.SELF, EStructuralFeature.class)
.map(featureToDisplayName::get)
.orElse("");
Expand All @@ -189,7 +220,7 @@ private List<ColumnDescription> getColumnDescriptions() {
.isResizablePredicate(variableManager -> true)
.filterVariantProvider(variableManager -> "text")
.build();
return List.of(iconColumnDescription, columnDescription);
return List.of(iconColumnDescription, columnDescription, nbAnnotationsColumn);
}

private List<ICellDescription> getCellDescriptions() {
Expand Down Expand Up @@ -240,6 +271,25 @@ private List<ICellDescription> getCellDescriptions() {
.cellValueProvider((variableManager, columnTargetObject) -> "")
.cellIconURLsProvider(iconLabelCellIconURLsProvider)
.build());

Predicate<VariableManager> nbAnnotationCellPredicate = variableManager -> variableManager.get(ColumnDescription.COLUMN_TARGET_OBJECT, Object.class)
.filter(String.class::isInstance)
.map(String.class::cast)
.filter(value -> value.equals("NbAnnotationColumn"))
.isPresent();

cellDescriptions.add(IconLabelCellDescription.newIconLabelCellDescription("nbAnnotationCells")
.canCreatePredicate(nbAnnotationCellPredicate)
.targetObjectIdProvider(variableManager -> "")
.targetObjectKindProvider(variableManager -> "")
.cellValueProvider((variableManager, columnTargetObject) -> variableManager.get(VariableManager.SELF, Object.class)
.filter(AnnotableElement.class::isInstance)
.map(AnnotableElement.class::cast)
.map(annotableElement -> annotableElement.getAnnotations().size())
.map(String::valueOf)
.orElse("NA"))
.cellIconURLsProvider((variableManager, o) -> List.of())
.build());
return cellDescriptions;
}
}
6 changes: 5 additions & 1 deletion packages/tables/backend/sirius-components-tables/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
<version>2025.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -113,4 +117,4 @@
</plugin>
</plugins>
</build>
</project>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2024 CEA LIST.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.tables;

import java.util.List;
import java.util.Objects;

/**
* Data representing the filter of a column with column mapped to the targetObjectId.
*
* @author frouene
*/
public record ColumnFilterMapped(String id, List<String> values) {

public ColumnFilterMapped {
Objects.requireNonNull(id);
Objects.requireNonNull(values);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
*******************************************************************************/
package org.eclipse.sirius.components.tables.components;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
Expand All @@ -23,6 +27,7 @@
import org.eclipse.sirius.components.representations.IComponent;
import org.eclipse.sirius.components.representations.VariableManager;
import org.eclipse.sirius.components.tables.ColumnFilter;
import org.eclipse.sirius.components.tables.ColumnFilterMapped;
import org.eclipse.sirius.components.tables.PaginationData;
import org.eclipse.sirius.components.tables.Table;
import org.eclipse.sirius.components.tables.descriptions.PaginatedData;
Expand Down Expand Up @@ -78,12 +83,28 @@ public Element render() {
return new Element(ColumnComponent.class, columnComponentProps);
})
.toList();

ObjectMapper objectMapper = new ObjectMapper();
var columnFiltersMapped = optionalPreviousTable
.map(previousTable -> columnsFilters.stream()
.map(columnFilter ->
tableElementRequestor.getColumn(previousTable, columnFilter.id())
.map(column -> new ColumnFilter(column.getTargetObjectId(), columnFilter.value()))
.map(column -> {
List<String> columnFilterValues = new ArrayList<>();
if (List.of("range", "range-slider").contains(column.getFilterVariant())) {
try {
columnFilterValues.addAll(objectMapper.readValue(columnFilter.value(), new TypeReference<>() {
}));
} catch (JsonProcessingException ignored) {
}
} else {
try {
columnFilterValues.add(objectMapper.readValue(columnFilter.value(), new TypeReference<>() {
}));
} catch (JsonProcessingException ignored) {
}
}
return new ColumnFilterMapped(column.getTargetObjectId(), columnFilterValues);
})
)
.filter(Optional::isPresent)
.map(Optional::get)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ export const useTableColumnFiltering = (
}, [columnFilters]);

useEffect(() => {
setColumnFilters(table.columnFilters);
setColumnFilters(
table.columnFilters.map((filter) => ({
id: filter.id,
value: JSON.parse(filter.value as string),
}))
);
}, [table.columnFilters.map((columnFilter) => columnFilter.id + columnFilter.value).join('')]);

const [mutationChangeColumnFilter, mutationChangeColumnFilterResult] = useMutation<
Expand All @@ -74,14 +79,17 @@ export const useTableColumnFiltering = (
editingContextId,
representationId,
tableId: table.id,
columnFilters: columnFilters.map((columnFilter) => ({ id: columnFilter.id, value: columnFilter.value })),
columnFilters: columnFilters.map((columnFilter) => ({
id: columnFilter.id,
value: JSON.stringify(columnFilter.value),
})),
};
mutationChangeColumnFilter({ variables: { input } });
};

if (!enableColumnFilters) {
return {
columnFilters: undefined,
columnFilters: [],
setColumnFilters: undefined,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const tableIdProvider = (
columnFilters !== null
? `&columnFilters=[${columnFilters
.map((filter) => {
return filter.id + ':' + filter.value;
return filter.id + ':' + JSON.stringify(filter.value);
})
.map(encodeURIComponent)
.join(',')}]`
Expand Down

0 comments on commit 55cb3ac

Please sign in to comment.