Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4352] Add support to range column filters in table #4353

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Note that you can retrieve the URL of a page in the response header of `GET /api
Note that you may need to encode special characters like `[`(by `%5B`) and `]` (by `%5D`) in your requests.
- https://github.com/eclipse-sirius/sirius-web/issues/4381[#4381] [trees] Set activeFilterIds variable in DefaultExpandAllTreePathHandler.
- https://github.com/eclipse-sirius/sirius-web/issues/3533[#3553] [diagram] Add a dedicated component to handle diagram subscription

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

== v2025.1.0

Expand Down Expand Up @@ -220,7 +220,7 @@ These components could be reused by downstream applications in custom creation t
- https://github.com/eclipse-sirius/sirius-web/issues/4330[#4330] [diagram] Improve responsiveness when executing a tool from the palette
- https://github.com/eclipse-sirius/sirius-web/issues/4333[#4333] [diagram] Improve the style of the palette by switching the positions of the search field and the quick access tools and by adding a small touch of grey in the header bar and search field
- https://github.com/eclipse-sirius/sirius-web/issues/4286[#4286] [sirius-web] Make default explorer drag and drop work only for the default explorer.
Downstream applications with a custom explorer that relies on `ExplorerDropTreeItemHandler` now need to provide their own `IDropTreeItemHandler` to support drag and drop in their explorer.
Downstream applications with a custom explorer that relies on `ExplorerDropTreeItemHandler` now need to provide their own `IDropTreeItemHandler` to support drag and drop in their explorer.


== v2024.11.0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021, 2024 Obeo.
* Copyright (c) 2021, 2025 Obeo.
* 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
Expand All @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain me what this change of behavior supposed to do? It's a bit complex to understand just from a Quick Look at the code, what is the difference in the old vs new behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the filter values of a column can be a list, we end up with uri of this type [467ec1c3-8ba7-32dc-9d72-71a2ccad161b:["0","1"],4752c1c3-8ba7-32dc-9d72-71a2ccad161b:["1","2"]]
The previous implementation of getParameterEntries split each , whiche end up giving something like 467ec1c3-8ba7-32dc-9d72-71a2ccad161b:["0" ,"1"] ...
In the new approach, I consider the level of [ to only split the first list.

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) 2025 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
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* Copyright (c) 2024, 2025 Obeo.
* 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
Expand All @@ -12,6 +12,10 @@
*******************************************************************************/
package org.eclipse.sirius.web.papaya.representations.table;

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.Map;
Expand All @@ -29,6 +33,7 @@
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;
Expand Down Expand Up @@ -125,7 +130,7 @@ private PaginatedData getSemanticElements(VariableManager variableManager) {
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());

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

});
}
Expand All @@ -158,14 +193,26 @@ private List<ColumnDescription> getColumnDescriptions() {
.semanticElementsProvider(variableManager -> List.of("IconColumn"))
.headerLabelProvider(variableManager -> "Icon")
.headerIconURLsProvider(variableManager -> List.of("/icons/svg/Default.svg"))
.headerIndexLabelProvider(variableManager -> "")
.headerIndexLabelProvider(variableManager -> "A")
.targetObjectIdProvider(new ColumnTargetObjectIdProvider())
.targetObjectKindProvider(variableManager -> "")
.initialWidthProvider(variableManager -> 130)
.isResizablePredicate(variableManager -> false)
.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 -> "F")
.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 @@ -175,7 +222,7 @@ private List<ColumnDescription> getColumnDescriptions() {
.orElse(List.of());

Function<VariableManager, String> headerIndexLabelProvider = variableManager -> variableManager.get("columnIndex", Integer.class)
.map(index -> String.valueOf((char) (index + 'A')))
.map(index -> String.valueOf((char) (index + 1 + 'A')))
.orElse("");

ColumnDescription columnDescription = ColumnDescription.newColumnDescription(UUID.nameUUIDFromBytes("features".getBytes()).toString())
Expand All @@ -189,7 +236,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 +287,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;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2024 CEA LIST.
* Copyright (c) 2024, 2025 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
Expand Down Expand Up @@ -106,7 +106,7 @@ public void givenTableWhenColumnResizeMutationTriggeredThenTheRepresentationIsRe
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getColumns()).hasSize(5);
assertThat(table.getColumns()).hasSize(6);
columnRef.set(table.getColumns().get(1));
assertThat(table.getColumns().get(1).getWidth()).isEqualTo(180);
tableId.set(table.getId());
Expand All @@ -131,7 +131,7 @@ public void givenTableWhenColumnResizeMutationTriggeredThenTheRepresentationIsRe
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getColumns()).hasSize(5);
assertThat(table.getColumns()).hasSize(6);
assertThat(table.getColumns().get(1).getWidth()).isEqualTo(50);
}, () -> fail(MISSING_TABLE));

Expand Down Expand Up @@ -162,7 +162,7 @@ public void givenTableWhenColumnVisibilityChangesMutationTriggeredThenTheReprese
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getColumns()).hasSize(5);
assertThat(table.getColumns()).hasSize(6);
columnNameRef.set(table.getColumns().get(0));
columnDescRef.set(table.getColumns().get(1));
columnAnnotationRef.set(table.getColumns().get(2));
Expand Down Expand Up @@ -195,7 +195,7 @@ public void givenTableWhenColumnVisibilityChangesMutationTriggeredThenTheReprese
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getColumns()).hasSize(5);
assertThat(table.getColumns()).hasSize(6);
assertThat(table.getColumns().get(0).isHidden()).isTrue();
assertThat(table.getColumns().get(1).isHidden()).isFalse();
assertThat(table.getColumns().get(2).isHidden()).isTrue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public void givenTableRepresentationWhenWeSubscribeToItsEventThenTheRepresentati
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getColumns()).hasSize(5);
assertThat(table.getColumns()).hasSize(6);
assertThat(table.getLines()).hasSize(2);
}, () -> fail(MISSING_TABLE));

Expand All @@ -148,7 +148,7 @@ public void givenTableWhenRefreshTriggeredThenTableIsRefreshed() {
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getColumns()).hasSize(5);
assertThat(table.getColumns()).hasSize(6);
assertThat(table.getLines()).hasSize(2);

tableId.set(table.getId());
Expand Down Expand Up @@ -199,12 +199,13 @@ public void givenTableRepresentationWithColumnHeaderWhenWeSubscribeToItsEventThe
.map(TableRefreshedEventPayload::table)
.ifPresentOrElse(table -> {
assertThat(table).isNotNull();
assertThat(table.getColumns()).hasSize(5);
assertThat(table.getColumns().get(0).getHeaderIndexLabel()).isEqualTo("");
assertThat(table.getColumns().get(1).getHeaderIndexLabel()).isEqualTo("A");
assertThat(table.getColumns().get(2).getHeaderIndexLabel()).isEqualTo("B");
assertThat(table.getColumns().get(3).getHeaderIndexLabel()).isEqualTo("C");
assertThat(table.getColumns().get(4).getHeaderIndexLabel()).isEqualTo("D");
assertThat(table.getColumns()).hasSize(6);
assertThat(table.getColumns().get(0).getHeaderIndexLabel()).isEqualTo("A");
assertThat(table.getColumns().get(1).getHeaderIndexLabel()).isEqualTo("B");
assertThat(table.getColumns().get(2).getHeaderIndexLabel()).isEqualTo("C");
assertThat(table.getColumns().get(3).getHeaderIndexLabel()).isEqualTo("D");
assertThat(table.getColumns().get(4).getHeaderIndexLabel()).isEqualTo("E");
assertThat(table.getColumns().get(5).getHeaderIndexLabel()).isEqualTo("F");
}, () -> fail(MISSING_TABLE));

StepVerifier.create(flux)
Expand Down
Loading
Loading