Skip to content

Commit 3728214

Browse files
Add 2 new filters for lists (#2703)
* Fix #2061 - Added Contains under the Attribute category in Filters * Fix issue with missing Bundle * Add option on partition filter for lists and add tests Co-authored-by: Sukankana Chakraborty <[email protected]>
1 parent 43b73bc commit 3728214

File tree

12 files changed

+615
-9
lines changed

12 files changed

+615
-9
lines changed

modules/FiltersPlugin/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@
6060
<groupId>${project.groupId}</groupId>
6161
<artifactId>project-api</artifactId>
6262
</dependency>
63+
<!-- Test only -->
64+
<dependency>
65+
<groupId>${project.groupId}</groupId>
66+
<artifactId>graph-api</artifactId>
67+
<scope>test</scope>
68+
<type>test-jar</type>
69+
</dependency>
6370
</dependencies>
6471

6572
<build>
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
Copyright 2008-2010 Gephi
3+
Authors : Mathieu Bastian <[email protected]>
4+
Website : http://www.gephi.org
5+
6+
This file is part of Gephi.
7+
8+
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
9+
10+
Copyright 2011 Gephi Consortium. All rights reserved.
11+
12+
The contents of this file are subject to the terms of either the GNU
13+
General Public License Version 3 only ("GPL") or the Common
14+
Development and Distribution License("CDDL") (collectively, the
15+
"License"). You may not use this file except in compliance with the
16+
License. You can obtain a copy of the License at
17+
http://gephi.org/about/legal/license-notice/
18+
or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
19+
specific language governing permissions and limitations under the
20+
License. When distributing the software, include this License Header
21+
Notice in each file and include the License files at
22+
/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
23+
License Header, with the fields enclosed by brackets [] replaced by
24+
your own identifying information:
25+
"Portions Copyrighted [year] [name of copyright owner]"
26+
27+
If you wish your version of this file to be governed by only the CDDL
28+
or only the GPL Version 3, indicate your decision by adding
29+
"[Contributor] elects to include this software in this distribution
30+
under the [CDDL or GPL Version 3] license." If you do not indicate a
31+
single choice of license, a recipient has the option to distribute
32+
your version of this file under either the CDDL, the GPL Version 3 or
33+
to extend the choice of license to its licensees as provided above.
34+
However, if you add GPL Version 3 code and therefore, elected the GPL
35+
Version 3 license, then the option applies only if the new code is
36+
made subject to such option by the copyright holder.
37+
38+
Contributor(s):
39+
40+
Portions Copyrighted 2011 Gephi Consortium.
41+
*/
42+
43+
package org.gephi.filters.plugin.attribute;
44+
45+
import java.lang.reflect.Array;
46+
import java.util.ArrayList;
47+
import java.util.List;
48+
import javax.swing.JPanel;
49+
import org.gephi.filters.api.FilterLibrary;
50+
import org.gephi.filters.plugin.AbstractAttributeFilter;
51+
import org.gephi.filters.plugin.AbstractAttributeFilterBuilder;
52+
import org.gephi.filters.spi.Category;
53+
import org.gephi.filters.spi.CategoryBuilder;
54+
import org.gephi.filters.spi.EdgeFilter;
55+
import org.gephi.filters.spi.Filter;
56+
import org.gephi.filters.spi.FilterBuilder;
57+
import org.gephi.filters.spi.NodeFilter;
58+
import org.gephi.graph.api.AttributeUtils;
59+
import org.gephi.graph.api.Column;
60+
import org.gephi.graph.api.Element;
61+
import org.gephi.graph.api.Graph;
62+
import org.gephi.graph.api.GraphController;
63+
import org.gephi.graph.api.GraphModel;
64+
import org.gephi.project.api.Workspace;
65+
import org.openide.util.Lookup;
66+
import org.openide.util.NbBundle;
67+
import org.openide.util.lookup.ServiceProvider;
68+
69+
/**
70+
* @author Sukankana Chakraborty
71+
*/
72+
@ServiceProvider(service = CategoryBuilder.class)
73+
public class ListAttributeContainsBuilder implements CategoryBuilder {
74+
75+
private final static Category CONTAINS = new Category(
76+
NbBundle.getMessage(ListAttributeContainsBuilder.class, "AttributeContainsBuilder.name"),
77+
null,
78+
FilterLibrary.ATTRIBUTES);
79+
80+
@Override
81+
public Category getCategory() {
82+
return CONTAINS;
83+
}
84+
85+
@Override
86+
public FilterBuilder[] getBuilders(Workspace workspace) {
87+
List<FilterBuilder> builders = new ArrayList<>();
88+
GraphModel am = Lookup.getDefault().lookup(GraphController.class).getGraphModel(workspace);
89+
for (Column col : am.getNodeTable()) {
90+
if (col.isArray()) {
91+
AttributeContainsFilterBuilder b = new AttributeContainsFilterBuilder(col);
92+
builders.add(b);
93+
}
94+
}
95+
for (Column col : am.getEdgeTable()) {
96+
if (col.isArray()) {
97+
AttributeContainsFilterBuilder b = new AttributeContainsFilterBuilder(col);
98+
builders.add(b);
99+
}
100+
}
101+
return builders.toArray(new FilterBuilder[0]);
102+
}
103+
104+
public static class AttributeContainsFilterBuilder extends AbstractAttributeFilterBuilder {
105+
106+
public AttributeContainsFilterBuilder(Column column) {
107+
super(column,
108+
CONTAINS,
109+
NbBundle.getMessage(AttributeEqualBuilder.class, "AttributeContainsBuilder.description"),
110+
null);
111+
}
112+
113+
@Override
114+
public AttributeContainsFilter getFilter(Workspace workspace) {
115+
return AttributeUtils.isNodeColumn(column) ? new AttributeContainsFilter.Node(column) :
116+
new AttributeContainsFilter.Edge(column);
117+
}
118+
119+
@Override
120+
public JPanel getPanel(Filter filter) {
121+
return Lookup.getDefault().lookup(ListContainsUI.class).getPanel((AttributeContainsFilter) filter);
122+
}
123+
}
124+
125+
public static abstract class AttributeContainsFilter<K extends Element> extends AbstractAttributeFilter<K> {
126+
127+
private Object match;
128+
129+
public AttributeContainsFilter(Column column) {
130+
super(NbBundle.getMessage(AttributeEqualBuilder.class, "AttributeContainsBuilder.name"),
131+
column);
132+
133+
addProperty(Object.class, "match");
134+
}
135+
136+
@Override
137+
public boolean init(Graph graph) {
138+
if (AttributeUtils.isNodeColumn(column)) {
139+
return graph.getNodeCount() != 0;
140+
} else if (AttributeUtils.isEdgeColumn(column)) {
141+
return graph.getEdgeCount() != 0;
142+
}
143+
return true;
144+
}
145+
146+
@Override
147+
public boolean evaluate(Graph graph, Element element) {
148+
if (match != null) {
149+
Object array = element.getAttribute(column);
150+
if (array != null) {
151+
int length = Array.getLength(array);
152+
Class componentType = array.getClass().getComponentType();
153+
Class matchType = match.getClass();
154+
boolean sameType = componentType.equals(matchType);
155+
for (int i = 0; i < length; i++) {
156+
Object val = Array.get(array, i);
157+
if (sameType) {
158+
if (val.equals(match)) {
159+
return true;
160+
}
161+
} else {
162+
if (val.equals(AttributeUtils.parse(match.toString(), componentType))) {
163+
return true;
164+
}
165+
}
166+
}
167+
}
168+
}
169+
return false;
170+
}
171+
172+
@Override
173+
public void finish() {
174+
}
175+
176+
public void setMatch(Object match) {
177+
this.match = match;
178+
}
179+
180+
public Object getMatch() {
181+
return match;
182+
}
183+
184+
public static class Node extends AttributeContainsFilter<org.gephi.graph.api.Node> implements NodeFilter {
185+
186+
public Node(Column column) {
187+
super(column);
188+
}
189+
}
190+
191+
public static class Edge extends AttributeContainsFilter<org.gephi.graph.api.Edge> implements EdgeFilter {
192+
193+
public Edge(Column column) {
194+
super(column);
195+
}
196+
}
197+
}
198+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.gephi.filters.plugin.attribute;
2+
3+
import javax.swing.JPanel;
4+
5+
public interface ListContainsUI {
6+
7+
JPanel getPanel(ListAttributeContainsBuilder.AttributeContainsFilter filter);
8+
}

modules/FiltersPlugin/src/main/java/org/gephi/filters/plugin/partition/PartitionBuilder.java

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,12 @@ Development and Distribution License("CDDL") (collectively, the
4242

4343
package org.gephi.filters.plugin.partition;
4444

45+
import java.lang.reflect.Array;
4546
import java.util.ArrayList;
47+
import java.util.Collection;
4648
import java.util.HashSet;
4749
import java.util.List;
50+
import java.util.Objects;
4851
import java.util.Set;
4952
import javax.swing.Icon;
5053
import javax.swing.JPanel;
@@ -231,6 +234,7 @@ public static abstract class PartitionFilter implements Filter {
231234
protected FilterProperty[] filterProperties;
232235
protected Set<Object> parts;
233236
protected Graph graph;
237+
protected boolean flattenList;
234238

235239
public PartitionFilter(Partition partition) {
236240
this(null, partition);
@@ -252,15 +256,30 @@ public boolean evaluate(Graph graph, Node node) {
252256
Object value = partition.getValue(node, graph);
253257
if (value == null) {
254258
return parts.contains(NULL);
259+
} else if (flattenList && partition.getColumn().isArray()) {
260+
return listContains(value);
255261
} else {
256262
return parts.contains(value);
257263
}
258264
}
259265

266+
private boolean listContains(Object value) {
267+
int length = Array.getLength(value);
268+
for (int i = 0; i < length; i++) {
269+
Object val = Array.get(value, i);
270+
if (parts.contains(val)) {
271+
return true;
272+
}
273+
}
274+
return false;
275+
}
276+
260277
public boolean evaluate(Graph graph, Edge edge) {
261278
Object value = partition.getValue(edge, graph);
262279
if (value == null) {
263280
return parts.contains(NULL);
281+
} else if (flattenList && partition.getColumn().isArray()) {
282+
return listContains(value);
264283
} else {
265284
return parts.contains(value);
266285
}
@@ -293,8 +312,23 @@ public void unselectAll() {
293312
getProperties()[1].setValue(new HashSet<>());
294313
}
295314

315+
public Set getFlattenParts() {
316+
HashSet allParts = new HashSet();
317+
partition.getValues(graph).stream().filter(Objects::nonNull).forEach(value -> {
318+
int length = Array.getLength(value);
319+
for (int i = 0; i < length; i++) {
320+
allParts.add(Array.get(value, i));
321+
}
322+
});
323+
return allParts;
324+
}
325+
296326
public void selectAll() {
297-
getProperties()[1].setValue(new HashSet<>(partition.getValues(graph)));
327+
if (flattenList && partition.getColumn().isArray()) {
328+
getProperties()[1].setValue(getFlattenParts());
329+
} else {
330+
getProperties()[1].setValue(new HashSet<>(partition.getValues(graph)));
331+
}
298332
}
299333

300334
@Override
@@ -304,14 +338,23 @@ public FilterProperty[] getProperties() {
304338
try {
305339
filterProperties = new FilterProperty[] {
306340
FilterProperty.createProperty(this, Column.class, "column"),
307-
FilterProperty.createProperty(this, Set.class, "parts")};
341+
FilterProperty.createProperty(this, Set.class, "parts"),
342+
FilterProperty.createProperty(this, Boolean.class, "flattenList"),};
308343
} catch (Exception ex) {
309344
Exceptions.printStackTrace(ex);
310345
}
311346
}
312347
return filterProperties;
313348
}
314349

350+
public boolean isFlattenList() {
351+
return flattenList;
352+
}
353+
354+
public void setFlattenList(boolean flattenList) {
355+
this.flattenList = flattenList;
356+
}
357+
315358
public Partition getPartition() {
316359
return partition;
317360
}

modules/FiltersPlugin/src/main/resources/org/gephi/filters/plugin/attribute/Bundle.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@ AttributeEqualBuilder.name = Equal
55
AttributeEqualBuilder.description = Keep nodes/edges with particular value (String, Number, Boolean) for a column
66

77
AttributeNonNullBuilder.name = Non-null
8-
AttributeNonNullBuilder.description = Keep nodes/edges with non-null values for a particular column
8+
AttributeNonNullBuilder.description = Keep nodes/edges with non-null values for a particular column
9+
10+
AttributeContainsBuilder.name = List Contains
11+
AttributeContainsBuilder.description = Keep nodes/edges with a list column containing a particular value
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.gephi.filters.plugin.attribute;
2+
3+
import org.gephi.filters.spi.FilterBuilder;
4+
import org.gephi.graph.GraphGenerator;
5+
import org.gephi.graph.api.Graph;
6+
import org.gephi.graph.api.Node;
7+
import org.junit.Assert;
8+
import org.junit.Test;
9+
10+
public class ContainsTest {
11+
12+
@Test
13+
public void testStringArray() {
14+
GraphGenerator graphGenerator = GraphGenerator.build().withWorkspace().generateTinyGraph().addStringArrayNodeColumn();
15+
Graph graph = graphGenerator.getGraph();
16+
17+
ListAttributeContainsBuilder builder = new ListAttributeContainsBuilder();
18+
FilterBuilder[] builders = builder.getBuilders(graphGenerator.getWorkspace());
19+
Assert.assertEquals(1, builders.length);
20+
21+
ListAttributeContainsBuilder.AttributeContainsFilter<Node> filter =
22+
(ListAttributeContainsBuilder.AttributeContainsFilter<Node>) builders[0].getFilter(graphGenerator.getWorkspace());
23+
Assert.assertTrue(filter.getColumn().isArray());
24+
Assert.assertFalse(filter.evaluate(graphGenerator.getGraph(), graph.getNode(GraphGenerator.FIRST_NODE)));
25+
filter.setMatch(GraphGenerator.STRING_ARRAY_COLUMN_VALUES[0][0]);
26+
Assert.assertTrue(filter.evaluate(graphGenerator.getGraph(), graph.getNode(GraphGenerator.FIRST_NODE)));
27+
filter.setMatch("none");
28+
Assert.assertFalse(filter.evaluate(graphGenerator.getGraph(), graph.getNode(GraphGenerator.FIRST_NODE)));
29+
}
30+
31+
@Test
32+
public void testFloatArrayStringMatch() {
33+
GraphGenerator graphGenerator = GraphGenerator.build().withWorkspace().generateTinyGraph().addFloatArrayNodeColumn();
34+
Graph graph = graphGenerator.getGraph();
35+
36+
ListAttributeContainsBuilder builder = new ListAttributeContainsBuilder();
37+
FilterBuilder[] builders = builder.getBuilders(graphGenerator.getWorkspace());
38+
39+
ListAttributeContainsBuilder.AttributeContainsFilter<Node> filter =
40+
(ListAttributeContainsBuilder.AttributeContainsFilter<Node>) builders[0].getFilter(graphGenerator.getWorkspace());
41+
filter.setMatch("1.0");
42+
Assert.assertTrue(filter.evaluate(graphGenerator.getGraph(), graph.getNode(GraphGenerator.FIRST_NODE)));
43+
}
44+
45+
}

0 commit comments

Comments
 (0)