From 16928ca4f27e7d9d744b74bae15bacd28959d238 Mon Sep 17 00:00:00 2001 From: Rafael Troilo Date: Fri, 16 Jun 2023 15:53:21 +0200 Subject: [PATCH 01/11] adding missing getters --- .../filter/ChangesetIdFilterEqualsAnyOf.java | 12 +++++----- .../oshdb/filter/ChangesetIdFilterRange.java | 7 ++++++ .../oshdb/filter/IdFilterEqualsAnyOf.java | 22 ++++++++++++++----- .../ohsome/oshdb/filter/IdFilterRange.java | 7 ++++++ .../ohsome/oshdb/filter/TagFilterAnyOf.java | 21 ++++++++++++------ 5 files changed, 52 insertions(+), 17 deletions(-) diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterEqualsAnyOf.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterEqualsAnyOf.java index 76feb9201..914ec30b1 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterEqualsAnyOf.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterEqualsAnyOf.java @@ -14,11 +14,13 @@ * A filter which selects OSM contributions by matching to a list of changeset ids. */ public class ChangesetIdFilterEqualsAnyOf extends NegatableFilter { - private final Collection changesetIdList; + private final Collection changesetIds; ChangesetIdFilterEqualsAnyOf(@Nonnull Collection changesetIdList) { + this(new HashSet<>(changesetIdList)); + } + ChangesetIdFilterEqualsAnyOf(@Nonnull Set changesetIds) { super(new FilterInternal() { - private final Set changesetIds = new HashSet<>(changesetIdList); @Override public boolean applyOSH(OSHEntity entity) { @@ -37,15 +39,15 @@ public boolean applyOSMEntitySnapshot(OSMEntitySnapshot ignored) { @Override public String toString() { - return "changeset:in(" + changesetIdList.stream().map(String::valueOf) + return "changeset:in(" + changesetIds.stream().map(String::valueOf) .collect(Collectors.joining(",")) + ")"; } }); - this.changesetIdList = changesetIdList; + this.changesetIds = changesetIds; } @Contract(pure = true) public Collection getChangesetIdList() { - return this.changesetIdList; + return this.changesetIds; } } diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterRange.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterRange.java index 9b0cfb4fc..672b671c9 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterRange.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterRange.java @@ -8,6 +8,8 @@ * A filter which selects OSM contributions by matching to a range of changeset ids. */ public class ChangesetIdFilterRange extends NegatableFilter { + private final IdRange changesetIdRange; + ChangesetIdFilterRange(IdRange changesetIdRange) { super(new FilterInternal() { @Override @@ -30,5 +32,10 @@ public String toString() { return "changeset:in-range" + changesetIdRange; } }); + this.changesetIdRange = changesetIdRange; + } + + public IdRange getChangesetIdRange() { + return changesetIdRange; } } diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdFilterEqualsAnyOf.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdFilterEqualsAnyOf.java index 9904b9025..cd3d79b7f 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdFilterEqualsAnyOf.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdFilterEqualsAnyOf.java @@ -12,13 +12,19 @@ * A tag filter which executes a "id [not] in (id1, id2, …)" check. */ public class IdFilterEqualsAnyOf extends NegatableFilter { + + private final Set ids; + IdFilterEqualsAnyOf(@Nonnull Collection idList) { + this(new HashSet<>(idList)); + } + + IdFilterEqualsAnyOf(@Nonnull Set ids) { super(new FilterInternal() { - private final Set ids = new HashSet<>(idList); @Override public boolean applyOSH(OSHEntity entity) { - return this.ids.contains(entity.getId()); + return ids.contains(entity.getId()); } @Override @@ -28,7 +34,7 @@ boolean applyOSHNegated(OSHEntity entity) { @Override public boolean applyOSM(OSMEntity entity) { - return this.ids.contains(entity.getId()); + return ids.contains(entity.getId()); } @Override @@ -38,12 +44,18 @@ boolean applyOSMNegated(OSMEntity entity) { @Override public String toString() { - return "id:in" + this.ids.stream().map(String::valueOf).collect(Collectors.joining(",")); + return "id:in" + ids.stream().map(String::valueOf).collect(Collectors.joining(",")); } }); - if (idList.isEmpty()) { + if (ids.isEmpty()) { throw new IllegalStateException("list of ids must not be empty in a id in (list) filter"); } + + this.ids = ids; + } + + public Set getIds() { + return ids; } } \ No newline at end of file diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdFilterRange.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdFilterRange.java index ac332e888..3ff4679ae 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdFilterRange.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdFilterRange.java @@ -8,6 +8,8 @@ * A filter which executes a "id [not] in range" check. */ public class IdFilterRange extends NegatableFilter { + private final IdRange range; + IdFilterRange(@Nonnull IdRange range) { super(new FilterInternal() { @Override @@ -35,5 +37,10 @@ public String toString() { return "id:in-range" + range; } }); + this.range = range; + } + + public IdRange getRange() { + return range; } } \ No newline at end of file diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/TagFilterAnyOf.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/TagFilterAnyOf.java index 954522351..b57d04bc5 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/TagFilterAnyOf.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/TagFilterAnyOf.java @@ -12,19 +12,26 @@ */ abstract class TagFilterAnyOf implements Filter { final int keyId; - final Set tags; + final HashSet tags; TagFilterAnyOf(@Nonnull Collection tags) { Optional firstTag = tags.stream().findFirst(); - if (!firstTag.isPresent()) { - throw new IllegalStateException("list of tags must not be empty in a key in (values) filter"); - } else { - this.keyId = firstTag.get().getKey(); - this.tags = new HashSet<>(tags); - } + this.keyId = firstTag + .orElseThrow(() -> new IllegalStateException("list of tags must not be empty in a key in (values) filter")) + .getKey(); + this.tags = new HashSet<>(tags); + if (!tags.stream().allMatch(tag -> tag.getKey() == this.keyId)) { throw new IllegalStateException( "list of tags must all share the same tag key in a key in (values) filter"); } } + + public int getKeyId() { + return keyId; + } + + public Set getTags() { + return tags; + } } From 514a5cc525587880158f2c3f9b6dbf940e955a08 Mon Sep 17 00:00:00 2001 From: Rafael Troilo Date: Fri, 16 Jun 2023 15:55:23 +0200 Subject: [PATCH 02/11] minor fixes and changes for java 17 new features --- .../ohsome/oshdb/filter/FilterExpression.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterExpression.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterExpression.java index c1f23c763..bb06b2101 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterExpression.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterExpression.java @@ -81,7 +81,7 @@ default boolean applyOSMGeometry(OSMEntity entity, Geometry geometry) { * Apply a filter to a snapshot ({@link OSMEntitySnapshot}) of an OSM entity. * * @param snapshot a snapshot of the OSM entity to check - * @return true if the the OSM entity snapshot fulfills the specified filter, otherwise false. + * @return true if the OSM entity snapshot fulfills the specified filter, otherwise false. */ @Contract(pure = true) default boolean applyOSMEntitySnapshot(OSMEntitySnapshot snapshot) { @@ -95,7 +95,7 @@ default boolean applyOSMEntitySnapshot(OSMEntitySnapshot snapshot) { * modification or the state of it after the modification matches the filter.

* * @param contribution a modification of the OSM entity to check - * @return true if the the OSM contribution fulfills the specified filter, otherwise false. + * @return true if the OSM contribution fulfills the specified filter, otherwise false. */ @Contract(pure = true) default boolean applyOSMContribution(OSMContribution contribution) { @@ -133,11 +133,11 @@ default boolean applyOSMContribution(OSMContribution contribution) { */ @Contract(pure = true) default List> normalize() { - if (this instanceof Filter) { - return Collections.singletonList(Collections.singletonList((Filter) this)); - } else if (this instanceof AndOperator) { - List> exp1 = ((BinaryOperator) this).getLeftOperand().normalize(); - List> exp2 = ((BinaryOperator) this).getRightOperand().normalize(); + if (this instanceof Filter filter) { + return Collections.singletonList(Collections.singletonList(filter)); + } else if (this instanceof AndOperator operator) { + List> exp1 = operator.getLeftOperand().normalize(); + List> exp2 = operator.getRightOperand().normalize(); // return cross product of exp1 and exp2 List> combined = new LinkedList<>(); for (List e1 : exp1) { @@ -149,9 +149,9 @@ default List> normalize() { } } return combined; - } else if (this instanceof OrOperator) { - List> exp1 = ((BinaryOperator) this).getLeftOperand().normalize(); - List> exp2 = ((BinaryOperator) this).getRightOperand().normalize(); + } else if (this instanceof OrOperator operator) { + List> exp1 = operator.getLeftOperand().normalize(); + List> exp2 = operator.getRightOperand().normalize(); List> combined = new ArrayList<>(exp1.size() + exp2.size()); combined.addAll(exp1); combined.addAll(exp2); From b6421235b015850736746ba3bb7795ebda9625e0 Mon Sep 17 00:00:00 2001 From: Rafael Troilo Date: Fri, 16 Jun 2023 16:50:06 +0200 Subject: [PATCH 03/11] generate stream from IdRange --- .../main/java/org/heigit/ohsome/oshdb/filter/IdRange.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java index 4efc8b4f5..de10c7bda 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java @@ -1,6 +1,7 @@ package org.heigit.ohsome.oshdb.filter; import java.io.Serializable; +import java.util.stream.LongStream; /** * Helper class to handle ranges of ids (incl. user ids, changeset ids, etc.). @@ -34,4 +35,8 @@ public String toString() { + ".." + (toId == Long.MAX_VALUE ? "" : toId); } + + public LongStream getIds() { + return LongStream.range(fromId, toId+1); + } } From 4a9580a4438d180f425a79dc1cb2da779746a894 Mon Sep 17 00:00:00 2001 From: Rafael Troilo Date: Fri, 16 Jun 2023 16:51:04 +0200 Subject: [PATCH 04/11] new FilterUtil for extraction of ids from expression --- .../ohsome/oshdb/filter/FilterUtil.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java new file mode 100644 index 000000000..4d5d58a01 --- /dev/null +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java @@ -0,0 +1,36 @@ +package org.heigit.ohsome.oshdb.filter; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class FilterUtil { + + private FilterUtil() { + // utility class + } + + public static Set ids(FilterExpression expression) { + var normalized = expression.normalize(); + return ids(normalized); + } + + private static Set ids(List> normalized) { + var ids = new HashSet(); + for (var orGroups : normalized) { + for (var filter: orGroups) { + if (filter instanceof IdFilterEquals equals){ + ids.add(equals.getId()); + } else if (filter instanceof IdFilterEqualsAnyOf equalsAnyOf) { + ids.addAll(equalsAnyOf.getIds()); + } else if (filter instanceof IdFilterRange range) { + range.getRange().getIds().forEach(ids::add); + } else { + return Collections.emptySet(); + } + } + } + return ids; + } +} From 6d83b248010e2ae01f3e68ebdf7cacc4b6bb372c Mon Sep 17 00:00:00 2001 From: Rafael Troilo Date: Fri, 16 Jun 2023 17:09:20 +0200 Subject: [PATCH 05/11] new FilterUtil for extraction of ids from expression --- .../ohsome/oshdb/filter/FilterUtil.java | 21 +++++++------ .../ohsome/oshdb/filter/FilterUtilTest.java | 30 +++++++++++++++++++ 2 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java index 4d5d58a01..43aac13a2 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java @@ -1,10 +1,11 @@ package org.heigit.ohsome.oshdb.filter; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import static java.util.Collections.emptySet; + public class FilterUtil { private FilterUtil() { @@ -12,24 +13,26 @@ private FilterUtil() { } public static Set ids(FilterExpression expression) { - var normalized = expression.normalize(); - return ids(normalized); + return ids(expression.normalize()); } - private static Set ids(List> normalized) { + public static Set ids(List> normalized) { var ids = new HashSet(); for (var orGroups : normalized) { + var groupIds = new HashSet(); for (var filter: orGroups) { if (filter instanceof IdFilterEquals equals){ - ids.add(equals.getId()); + groupIds.add(equals.getId()); } else if (filter instanceof IdFilterEqualsAnyOf equalsAnyOf) { - ids.addAll(equalsAnyOf.getIds()); + groupIds.addAll(equalsAnyOf.getIds()); } else if (filter instanceof IdFilterRange range) { - range.getRange().getIds().forEach(ids::add); - } else { - return Collections.emptySet(); + range.getRange().getIds().forEach(groupIds::add); } } + if (groupIds.isEmpty()) { + return emptySet(); + } + ids.addAll(groupIds); } return ids; } diff --git a/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java new file mode 100644 index 000000000..bab47b59c --- /dev/null +++ b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java @@ -0,0 +1,30 @@ +package org.heigit.ohsome.oshdb.filter; + +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class FilterUtilTest extends FilterTest{ + + @Test + void extractIds() { + extractIds("id:123", Set.of(123L)); + extractIds("id:123 or id:234", Set.of(123L, 234L)); + extractIds("id:123 and not id:234", Set.of(123L)); + extractIds("id:123 or name=heigit", Set.of()); + extractIds("id:(123, 234)", Set.of(123L, 234L)); + extractIds("id:(123 .. 125)", Set.of(123L, 124L, 125L)); + } + + private void extractIds(String filter, Set expected) { + var expression = parser.parse(filter); + var actual = FilterUtil.ids(expression); + + assertEquals(expected.size(), actual.size()); + assertTrue(expected.containsAll(actual)); + } + +} \ No newline at end of file From 4529e568f11e269b762bd4e4fc0bc0e9cad75231 Mon Sep 17 00:00:00 2001 From: Rafael Troilo Date: Fri, 16 Jun 2023 18:12:03 +0200 Subject: [PATCH 06/11] changes for review --- .../filter/ChangesetIdFilterEqualsAnyOf.java | 4 +- .../ohsome/oshdb/filter/FilterUtil.java | 43 +++++++++++++------ .../heigit/ohsome/oshdb/filter/IdRange.java | 33 +++++++++++++- .../ohsome/oshdb/filter/TagFilterAnyOf.java | 2 +- .../ohsome/oshdb/filter/FilterUtilTest.java | 17 ++++---- 5 files changed, 72 insertions(+), 27 deletions(-) diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterEqualsAnyOf.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterEqualsAnyOf.java index 914ec30b1..2b6a62461 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterEqualsAnyOf.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/ChangesetIdFilterEqualsAnyOf.java @@ -14,7 +14,7 @@ * A filter which selects OSM contributions by matching to a list of changeset ids. */ public class ChangesetIdFilterEqualsAnyOf extends NegatableFilter { - private final Collection changesetIds; + private final Set changesetIds; ChangesetIdFilterEqualsAnyOf(@Nonnull Collection changesetIdList) { this(new HashSet<>(changesetIdList)); @@ -47,7 +47,7 @@ public String toString() { } @Contract(pure = true) - public Collection getChangesetIdList() { + public Set getChangesetIdList() { return this.changesetIds; } } diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java index 43aac13a2..d0b3ae384 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java @@ -1,10 +1,10 @@ package org.heigit.ohsome.oshdb.filter; -import java.util.HashSet; +import java.util.ArrayList; import java.util.List; -import java.util.Set; -import static java.util.Collections.emptySet; +import static java.util.Collections.emptyList; +import static java.util.Comparator.comparingLong; public class FilterUtil { @@ -12,28 +12,43 @@ private FilterUtil() { // utility class } - public static Set ids(FilterExpression expression) { + public static List ids(FilterExpression expression) { return ids(expression.normalize()); } - public static Set ids(List> normalized) { - var ids = new HashSet(); + public static List ids(List> normalized) { + var ranges = new ArrayList(); for (var orGroups : normalized) { - var groupIds = new HashSet(); + var groupRanges = new ArrayList(); for (var filter: orGroups) { if (filter instanceof IdFilterEquals equals){ - groupIds.add(equals.getId()); + groupRanges.add(new IdRange(equals.getId())); } else if (filter instanceof IdFilterEqualsAnyOf equalsAnyOf) { - groupIds.addAll(equalsAnyOf.getIds()); + equalsAnyOf.getIds().stream().map(IdRange::new).forEach(groupRanges::add); } else if (filter instanceof IdFilterRange range) { - range.getRange().getIds().forEach(groupIds::add); + groupRanges.add(range.getRange()); } } - if (groupIds.isEmpty()) { - return emptySet(); + if (groupRanges.isEmpty()) { + return emptyList(); } - ids.addAll(groupIds); + ranges.addAll(groupRanges); } - return ids; + var compact = new ArrayList(ranges.size()); + + ranges.sort(comparingLong(IdRange::getFromId)); + var itr = ranges.iterator(); + var range = itr.next(); + while (itr.hasNext()) { + var next = itr.next(); + if (next.getFromId() <= range.getToId() + 1) { + range = new IdRange(range.getFromId(), next.getToId()); + } else { + compact.add(range); + range = next; + } + } + compact.add(range); + return compact; } } diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java index de10c7bda..bf9d9e296 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java @@ -1,6 +1,7 @@ package org.heigit.ohsome.oshdb.filter; import java.io.Serializable; +import java.util.Objects; import java.util.stream.LongStream; /** @@ -9,7 +10,7 @@ *

The range's limits are tested inclusively: the range (10..12) would match the values 10, * 11 and 12, but not 9 or 13 for example.

*/ -class IdRange implements Serializable { +public class IdRange implements Serializable { private final long fromId; private final long toId; @@ -20,11 +21,15 @@ class IdRange implements Serializable { * @param fromId lower limit of the range. * @param toId upper limit of the range. */ - IdRange(long fromId, long toId) { + public IdRange(long fromId, long toId) { this.fromId = Math.min(fromId, toId); this.toId = Math.max(fromId, toId); } + public IdRange(long id) { + this(id, id); + } + /** Checks if the given id falls into the id range. */ public boolean test(long id) { return id >= fromId && id <= toId; @@ -36,6 +41,30 @@ public String toString() { + (toId == Long.MAX_VALUE ? "" : toId); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof IdRange idRange)) return false; + return fromId == idRange.fromId && toId == idRange.toId; + } + + @Override + public int hashCode() { + return Objects.hash(fromId, toId); + } + + public long getFromId() { + return fromId; + } + + public long getToId() { + return toId; + } + + public long size() { + return 1 + toId - fromId; + } + public LongStream getIds() { return LongStream.range(fromId, toId+1); } diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/TagFilterAnyOf.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/TagFilterAnyOf.java index b57d04bc5..e017bd348 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/TagFilterAnyOf.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/TagFilterAnyOf.java @@ -12,7 +12,7 @@ */ abstract class TagFilterAnyOf implements Filter { final int keyId; - final HashSet tags; + final Set tags; TagFilterAnyOf(@Nonnull Collection tags) { Optional firstTag = tags.stream().findFirst(); diff --git a/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java index bab47b59c..dc6bd73f2 100644 --- a/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java +++ b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java @@ -2,7 +2,7 @@ import org.junit.jupiter.api.Test; -import java.util.Set; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -11,15 +11,16 @@ class FilterUtilTest extends FilterTest{ @Test void extractIds() { - extractIds("id:123", Set.of(123L)); - extractIds("id:123 or id:234", Set.of(123L, 234L)); - extractIds("id:123 and not id:234", Set.of(123L)); - extractIds("id:123 or name=heigit", Set.of()); - extractIds("id:(123, 234)", Set.of(123L, 234L)); - extractIds("id:(123 .. 125)", Set.of(123L, 124L, 125L)); + extractIds("id:123", List.of(new IdRange(123L))); + extractIds("id:123 or id:234", List.of(new IdRange(123L), new IdRange(234L))); + extractIds("id:123 or id:124", List.of(new IdRange(123L, 124L))); + extractIds("id:123 and not id:234", List.of(new IdRange(123L))); + extractIds("id:123 or name=heigit", List.of()); + extractIds("id:(123, 234)", List.of(new IdRange(123L), new IdRange(234L))); + extractIds("id:(123 .. 125)", List.of(new IdRange(123L, 125L))); } - private void extractIds(String filter, Set expected) { + private void extractIds(String filter, List expected) { var expression = parser.parse(filter); var actual = FilterUtil.ids(expression); From bb87c508bae6b76ecf36a2cd495570ecb60205ff Mon Sep 17 00:00:00 2001 From: Rafael Troilo Date: Mon, 19 Jun 2023 15:05:51 +0200 Subject: [PATCH 07/11] Apply suggestions from code review Co-authored-by: Martin Raifer --- .../main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java | 2 +- .../src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java index d0b3ae384..1a51ab3cc 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java @@ -9,7 +9,7 @@ public class FilterUtil { private FilterUtil() { - // utility class + throw new IllegalStateException("utility class"); } public static List ids(FilterExpression expression) { diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java index bf9d9e296..fe8b2e7be 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/IdRange.java @@ -65,6 +65,7 @@ public long size() { return 1 + toId - fromId; } + /** Converts the id range to a stream of consecutive ids. */ public LongStream getIds() { return LongStream.range(fromId, toId+1); } From 9284e1a87fa98449dc017454fdae06c963774134 Mon Sep 17 00:00:00 2001 From: Rafael Troilo Date: Mon, 19 Jun 2023 15:25:34 +0200 Subject: [PATCH 08/11] add assert for idFilterEqualsAny.getIds() --- .../src/test/java/org/heigit/ohsome/oshdb/filter/ParseTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/ParseTest.java b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/ParseTest.java index 243322b79..7daeb3582 100644 --- a/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/ParseTest.java +++ b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/ParseTest.java @@ -158,6 +158,8 @@ void testIdFilterEqualsAnyOf() { FilterExpression expression = parser.parse("id:(1,2,3)"); assertTrue(expression instanceof IdFilterEqualsAnyOf); assertEquals("id:in1,2,3", expression.toString()); + var filter = (IdFilterEqualsAnyOf) expression; + assertTrue(filter.getIds().containsAll(List.of(1L, 2L, 3L))); } @Test From e335a4b851b2e76a4c900e98953b1ec51f45c233 Mon Sep 17 00:00:00 2001 From: Rafael Troilo Date: Mon, 19 Jun 2023 15:36:58 +0200 Subject: [PATCH 09/11] clean up and fix some warnings --- .../heigit/ohsome/oshdb/filter/ParseTest.java | 69 ++++++++----------- 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/ParseTest.java b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/ParseTest.java index 7daeb3582..5090719d7 100644 --- a/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/ParseTest.java +++ b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/ParseTest.java @@ -1,14 +1,5 @@ package org.heigit.ohsome.oshdb.filter; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; import org.heigit.ohsome.oshdb.OSHDBTag; import org.heigit.ohsome.oshdb.filter.GeometryTypeFilter.GeometryType; import org.heigit.ohsome.oshdb.osm.OSMType; @@ -16,6 +7,10 @@ import org.jparsec.error.ParserException; import org.junit.jupiter.api.Test; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; + /** * Tests the parsing of filters and the application to OSM entities. */ @@ -24,7 +19,7 @@ class ParseTest extends FilterTest { void testTagFilterEquals() { FilterExpression expression = parser.parse("highway=residential"); assertTrue(expression instanceof TagFilterEquals); - OSHDBTag tag = tagTranslator.getOSHDBTagOf("highway", "residential").get(); + OSHDBTag tag = tagTranslator.getOSHDBTagOf("highway", "residential").orElseThrow(); assertEquals(tag, ((TagFilterEquals) expression).getTag()); assertEquals("tag:" + tag.getKey() + "=" + tag.getValue(), expression.toString()); } @@ -44,7 +39,7 @@ void testTagFilterStrings() { void testTagFilterEqualsAny() { FilterExpression expression = parser.parse("highway=*"); assertTrue(expression instanceof TagFilterEqualsAny); - OSHDBTagKey tag = tagTranslator.getOSHDBTagKeyOf("highway").get(); + OSHDBTagKey tag = tagTranslator.getOSHDBTagKeyOf("highway").orElseThrow(); assertEquals(tag, ((TagFilterEqualsAny) expression).getTag()); assertEquals("tag:" + tag.toInt() + "=*", expression.toString()); } @@ -53,7 +48,7 @@ void testTagFilterEqualsAny() { void testTagFilterNotEquals() { FilterExpression expression = parser.parse("highway!=residential"); assertTrue(expression instanceof TagFilterNotEquals); - OSHDBTag tag = tagTranslator.getOSHDBTagOf("highway", "residential").get(); + OSHDBTag tag = tagTranslator.getOSHDBTagOf("highway", "residential").orElseThrow(); assertEquals(tag, ((TagFilterNotEquals) expression).getTag()); assertEquals("tag:" + tag.getKey() + "!=" + tag.getValue(), expression.toString()); } @@ -62,7 +57,7 @@ void testTagFilterNotEquals() { void testTagFilterNotEqualsAny() { FilterExpression expression = parser.parse("highway!=*"); assertTrue(expression instanceof TagFilterNotEqualsAny); - OSHDBTagKey tag = tagTranslator.getOSHDBTagKeyOf("highway").get(); + OSHDBTagKey tag = tagTranslator.getOSHDBTagKeyOf("highway").orElseThrow(); assertEquals(tag, ((TagFilterNotEqualsAny) expression).getTag()); assertEquals("tag:" + tag.toInt() + "!=*", expression.toString()); } @@ -72,8 +67,8 @@ void testTagFilterNotEqualsAny() { void testTagFilterEqualsAnyOf() { FilterExpression expression = parser.parse("highway in (residential, track)"); assertTrue(expression instanceof TagFilterEqualsAnyOf); - OSHDBTag tag1 = tagTranslator.getOSHDBTagOf("highway", "residential").get(); - OSHDBTag tag2 = tagTranslator.getOSHDBTagOf("highway", "track").get(); + OSHDBTag tag1 = tagTranslator.getOSHDBTagOf("highway", "residential").orElseThrow(); + OSHDBTag tag2 = tagTranslator.getOSHDBTagOf("highway", "track").orElseThrow(); assertTrue(expression.toString().matches("tag:" + tag1.getKey() + "in(" + tag1.getValue() + "," + tag2.getValue() + "|" + tag2.getValue() + "," + tag1.getValue() + ")" @@ -85,8 +80,8 @@ void testTagFilterEqualsAnyOf() { void testTagFilterNotEqualsAnyOf() { FilterExpression expression = parser.parse("not highway in (residential, track)"); assertTrue(expression instanceof TagFilterNotEqualsAnyOf); - OSHDBTag tag1 = tagTranslator.getOSHDBTagOf("highway", "residential").get(); - OSHDBTag tag2 = tagTranslator.getOSHDBTagOf("highway", "track").get(); + OSHDBTag tag1 = tagTranslator.getOSHDBTagOf("highway", "residential").orElseThrow(); + OSHDBTag tag2 = tagTranslator.getOSHDBTagOf("highway", "track").orElseThrow(); assertTrue(expression.toString().matches("tag:" + tag1.getKey() + "not-in(" + tag1.getValue() + "," + tag2.getValue() + "|" + tag2.getValue() + "," + tag1.getValue() + ")" @@ -95,36 +90,30 @@ void testTagFilterNotEqualsAnyOf() { @Test() void testTagFilterEqualsAnyOfCheckEmpty() { - assertThrows(IllegalStateException.class, () -> { - new TagFilterEqualsAnyOf(Collections.emptyList()); - }); + var list = Collections.emptyList(); + assertThrows(IllegalStateException.class, () -> new TagFilterEqualsAnyOf(list)); } @Test() void testTagFilterNotEqualsAnyOfCheckEmpty() { - assertThrows(IllegalStateException.class, () -> { - new TagFilterNotEqualsAnyOf(Collections.emptyList()); - }); + var list = Collections.emptyList(); + assertThrows(IllegalStateException.class, () -> new TagFilterNotEqualsAnyOf(list)); } @Test() void testTagFilterEqualsAnyOfCheckMixed() { - assertThrows(IllegalStateException.class, () -> { - new TagFilterEqualsAnyOf(Arrays.asList( - tagTranslator.getOSHDBTagOf("highway", "residential").get(), - tagTranslator.getOSHDBTagOf("building", "yes").get() - )); - }); + var list = Arrays.asList( + tagTranslator.getOSHDBTagOf("highway", "residential").orElseThrow(), + tagTranslator.getOSHDBTagOf("building", "yes").orElseThrow()); + assertThrows(IllegalStateException.class, () -> new TagFilterEqualsAnyOf(list)); } @Test() void testTagFilterNotEqualsAnyOfCheckMixed() { - assertThrows(IllegalStateException.class, () -> { - new TagFilterNotEqualsAnyOf(Arrays.asList( - tagTranslator.getOSHDBTagOf("highway", "residential").get(), - tagTranslator.getOSHDBTagOf("building", "yes").get() - )); - }); + var list = Arrays.asList( + tagTranslator.getOSHDBTagOf("highway", "residential").orElseThrow(), + tagTranslator.getOSHDBTagOf("building", "yes").orElseThrow()); + assertThrows(IllegalStateException.class, () -> new TagFilterNotEqualsAnyOf(list)); } @Test @@ -173,9 +162,8 @@ void testIdTypeFilterEqualsAnyOf() { @Test() void testIdFilterEqualsAnyOfCheckEmpty() { - assertThrows(IllegalStateException.class, () -> { - new IdFilterEqualsAnyOf(Collections.emptyList()); - }); + var emptyList = Collections.emptyList(); + assertThrows(IllegalStateException.class, () -> new IdFilterEqualsAnyOf(emptyList)); } @Test @@ -432,11 +420,8 @@ void testContributorUserIdRangeFilter() { assertEquals("contributor:in-range10..12", expression.toString()); } - @SuppressWarnings("ResultOfMethodCallIgnored") @Test() void testContributorIdFilterNotEnabled() { - assertThrows(ParserException.class, () -> { - parser.parse("contributor:0"); - }); + assertThrows(ParserException.class, () -> parser.parse("contributor:0")); } } From bdb936b3fe1951b9cd72ab02174cabf76391fc65 Mon Sep 17 00:00:00 2001 From: Rafael Troilo Date: Mon, 19 Jun 2023 17:54:00 +0200 Subject: [PATCH 10/11] incooperate typefilter for id extraction as well --- .../ohsome/oshdb/filter/FilterUtil.java | 71 +++++++++++-------- .../ohsome/oshdb/filter/FilterUtilTest.java | 44 +++++++++--- 2 files changed, 76 insertions(+), 39 deletions(-) diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java index 1a51ab3cc..827143da3 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java @@ -1,9 +1,10 @@ package org.heigit.ohsome.oshdb.filter; -import java.util.ArrayList; -import java.util.List; +import org.heigit.ohsome.oshdb.osm.OSMType; -import static java.util.Collections.emptyList; +import java.util.*; + +import static java.util.Collections.*; import static java.util.Comparator.comparingLong; public class FilterUtil { @@ -12,43 +13,53 @@ private FilterUtil() { throw new IllegalStateException("utility class"); } - public static List ids(FilterExpression expression) { + public static Map, List> ids(FilterExpression expression) { return ids(expression.normalize()); } - public static List ids(List> normalized) { - var ranges = new ArrayList(); - for (var orGroups : normalized) { + public static Map, List> ids(List> normalized) { + var result = new HashMap, List>(); + for (var group : normalized) { + var type = EnumSet.allOf(OSMType.class); var groupRanges = new ArrayList(); - for (var filter: orGroups) { - if (filter instanceof IdFilterEquals equals){ - groupRanges.add(new IdRange(equals.getId())); - } else if (filter instanceof IdFilterEqualsAnyOf equalsAnyOf) { - equalsAnyOf.getIds().stream().map(IdRange::new).forEach(groupRanges::add); - } else if (filter instanceof IdFilterRange range) { - groupRanges.add(range.getRange()); + for (var filter: group) { + if (filter instanceof TypeFilter typeFilter) { + type = EnumSet.of(typeFilter.getType()); + } else if (filter instanceof IdFilterEquals idFilter){ + groupRanges.add(new IdRange(idFilter.getId())); + } else if (filter instanceof IdFilterEqualsAnyOf idFilter) { + idFilter.getIds().stream().map(IdRange::new).forEach(groupRanges::add); + } else if (filter instanceof IdFilterRange idFilter) { + groupRanges.add(idFilter.getRange()); } } if (groupRanges.isEmpty()) { - return emptyList(); + return emptyMap(); } - ranges.addAll(groupRanges); + result.computeIfAbsent(type, x -> new ArrayList<>()).addAll(groupRanges); } - var compact = new ArrayList(ranges.size()); - - ranges.sort(comparingLong(IdRange::getFromId)); - var itr = ranges.iterator(); - var range = itr.next(); - while (itr.hasNext()) { - var next = itr.next(); - if (next.getFromId() <= range.getToId() + 1) { - range = new IdRange(range.getFromId(), next.getToId()); - } else { - compact.add(range); - range = next; + compactRanges(result); + return result; + } + + private static void compactRanges(HashMap, List> result) { + for (var entry : result.entrySet()) { + var ranges = entry.getValue(); + var compact = new ArrayList(ranges.size()); + ranges.sort(comparingLong(IdRange::getFromId)); + var itr = ranges.iterator(); + var range = itr.next(); + while (itr.hasNext()) { + var next = itr.next(); + if (next.getFromId() <= range.getToId() + 1) { + range = new IdRange(range.getFromId(), next.getToId()); + } else { + compact.add(range); + range = next; + } } + compact.add(range); + entry.setValue(compact); } - compact.add(range); - return compact; } } diff --git a/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java index dc6bd73f2..b149e6119 100644 --- a/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java +++ b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java @@ -1,8 +1,11 @@ package org.heigit.ohsome.oshdb.filter; +import org.heigit.ohsome.oshdb.osm.OSMType; import org.junit.jupiter.api.Test; +import java.util.EnumSet; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -11,21 +14,44 @@ class FilterUtilTest extends FilterTest{ @Test void extractIds() { - extractIds("id:123", List.of(new IdRange(123L))); - extractIds("id:123 or id:234", List.of(new IdRange(123L), new IdRange(234L))); - extractIds("id:123 or id:124", List.of(new IdRange(123L, 124L))); - extractIds("id:123 and not id:234", List.of(new IdRange(123L))); - extractIds("id:123 or name=heigit", List.of()); - extractIds("id:(123, 234)", List.of(new IdRange(123L), new IdRange(234L))); - extractIds("id:(123 .. 125)", List.of(new IdRange(123L, 125L))); + extractIds("id:123", Map.of(EnumSet.allOf(OSMType.class),List.of(new IdRange(123L)))); + extractIds("id:123 or id:234", Map.of(EnumSet.allOf(OSMType.class),List.of(new IdRange(123L), new IdRange(234L)))); + extractIds("id:123 or id:124", Map.of(EnumSet.allOf(OSMType.class),List.of(new IdRange(123L, 124L)))); + extractIds("id:123 and not id:234", Map.of(EnumSet.allOf(OSMType.class),List.of(new IdRange(123L)))); + extractIds("id:123 or name=heigit", Map.of()); + extractIds("id:(123, 234)", Map.of(EnumSet.allOf(OSMType.class),List.of(new IdRange(123L), new IdRange(234L)))); + extractIds("id:(123 .. 125)", Map.of(EnumSet.allOf(OSMType.class),List.of(new IdRange(123L, 125L)))); } - private void extractIds(String filter, List expected) { + @Test + void extractIdsWithTypes() { + extractIds("id:node/123 or id:way/234", Map.of( + EnumSet.of(OSMType.NODE), List.of(new IdRange(123L)), + EnumSet.of(OSMType.WAY), List.of(new IdRange(234L)))); + extractIds("id:node/123 or id:way/234 or id:way/235", Map.of( + EnumSet.of(OSMType.NODE), List.of(new IdRange(123L)), + EnumSet.of(OSMType.WAY), List.of(new IdRange(234L, 235L)))); + extractIds("id:node/123 or (type:way and id:234)", Map.of( + EnumSet.of(OSMType.NODE), List.of(new IdRange(123L)), + EnumSet.of(OSMType.WAY), List.of(new IdRange(234L)))); + extractIds("id:node/123 or id:234", Map.of( + EnumSet.of(OSMType.NODE), List.of(new IdRange(123L)), + EnumSet.allOf(OSMType.class), List.of(new IdRange(234L)))); + extractIds("not type:node and id:123", Map.of( + EnumSet.of(OSMType.WAY), List.of(new IdRange(123L)), + EnumSet.of(OSMType.RELATION), List.of(new IdRange(123L)))); + } + + private void extractIds(String filter, Map,List> expected) { var expression = parser.parse(filter); var actual = FilterUtil.ids(expression); assertEquals(expected.size(), actual.size()); - assertTrue(expected.containsAll(actual)); + for (var entry : expected.entrySet()) { + assertTrue(actual.containsKey(entry.getKey())); + assertEquals(entry.getValue().size(), actual.get(entry.getKey()).size()); + assertTrue(actual.get(entry.getKey()).containsAll(entry.getValue())); + } } } \ No newline at end of file From d2df8622599d5596e69d907f0254e007e718e7ed Mon Sep 17 00:00:00 2001 From: Rafael Troilo Date: Mon, 19 Jun 2023 19:15:09 +0200 Subject: [PATCH 11/11] add tag extraction --- .../ohsome/oshdb/filter/FilterUtil.java | 67 ++++++++++++++----- .../ohsome/oshdb/filter/FilterUtilTest.java | 13 +++- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java index 827143da3..01717068b 100644 --- a/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java +++ b/oshdb-filter/src/main/java/org/heigit/ohsome/oshdb/filter/FilterUtil.java @@ -1,8 +1,12 @@ package org.heigit.ohsome.oshdb.filter; +import org.heigit.ohsome.oshdb.OSHDBTag; import org.heigit.ohsome.oshdb.osm.OSMType; import java.util.*; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; import static java.util.Collections.*; import static java.util.Comparator.comparingLong; @@ -13,39 +17,66 @@ private FilterUtil() { throw new IllegalStateException("utility class"); } - public static Map, List> ids(FilterExpression expression) { - return ids(expression.normalize()); + public static Map, Set> tags(FilterExpression expression, Predicate test) { + return tags(expression.normalize(), test); } - public static Map, List> ids(List> normalized) { - var result = new HashMap, List>(); - for (var group : normalized) { + public static Map, Set> tags(List> normalized, Predicate test) { + return extract(normalized, filter -> { + if (filter instanceof TagFilterEquals tagFilter) { + return Stream.of(tagFilter.getTag()).filter(test); + } else if (filter instanceof TagFilterEqualsAny tagFilter) { + return Stream.of(tagFilter.getTag()).map(key -> new OSHDBTag(key.toInt(), -1)).filter(test); + } else if (filter instanceof TagFilterEqualsAnyOf tagFilter) { + return tagFilter.tags.stream().filter(test); + } + return Stream.empty(); + }); + } + + private static Map, Set> extract(List> normalized, Function> extractor) { + var result = new HashMap, Set>(); + for(var group: normalized){ var type = EnumSet.allOf(OSMType.class); - var groupRanges = new ArrayList(); - for (var filter: group) { + var groupResult = new HashSet(); + for (var filter : group) { if (filter instanceof TypeFilter typeFilter) { type = EnumSet.of(typeFilter.getType()); - } else if (filter instanceof IdFilterEquals idFilter){ - groupRanges.add(new IdRange(idFilter.getId())); - } else if (filter instanceof IdFilterEqualsAnyOf idFilter) { - idFilter.getIds().stream().map(IdRange::new).forEach(groupRanges::add); - } else if (filter instanceof IdFilterRange idFilter) { - groupRanges.add(idFilter.getRange()); + } else { + extractor.apply(filter).forEach(groupResult::add); } } - if (groupRanges.isEmpty()) { + if (groupResult.isEmpty()) { return emptyMap(); } - result.computeIfAbsent(type, x -> new ArrayList<>()).addAll(groupRanges); + result.computeIfAbsent(type, x -> new HashSet<>()).addAll(groupResult); } + return result; + } + + public static Map, Set> extractIds(FilterExpression expression) { + return extractIds(expression.normalize()); + } + + public static Map, Set> extractIds(List> normalized) { + var result = extract(normalized, filter -> { + if (filter instanceof IdFilterEquals idFilter){ + return Stream.of(new IdRange(idFilter.getId())); + } else if (filter instanceof IdFilterEqualsAnyOf idFilter) { + return idFilter.getIds().stream().map(IdRange::new); + } else if (filter instanceof IdFilterRange idFilter) { + return Stream.of(idFilter.getRange()); + } + return Stream.empty(); + }); compactRanges(result); return result; } - private static void compactRanges(HashMap, List> result) { + private static void compactRanges(Map, Set> result) { for (var entry : result.entrySet()) { - var ranges = entry.getValue(); - var compact = new ArrayList(ranges.size()); + var ranges = new ArrayList<>(entry.getValue()); + var compact = new HashSet(ranges.size()); ranges.sort(comparingLong(IdRange::getFromId)); var itr = ranges.iterator(); var range = itr.next(); diff --git a/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java index b149e6119..0dd5ba963 100644 --- a/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java +++ b/oshdb-filter/src/test/java/org/heigit/ohsome/oshdb/filter/FilterUtilTest.java @@ -1,5 +1,6 @@ package org.heigit.ohsome.oshdb.filter; +import org.heigit.ohsome.oshdb.OSHDBTag; import org.heigit.ohsome.oshdb.osm.OSMType; import org.junit.jupiter.api.Test; @@ -42,9 +43,17 @@ void extractIdsWithTypes() { EnumSet.of(OSMType.RELATION), List.of(new IdRange(123L)))); } - private void extractIds(String filter, Map,List> expected) { + @Test + void extractTags() { + var expression = parser.parse("type:way and (highway=primary or building=*)"); + var result = FilterUtil.tags(expression, x -> true); + assertTrue(result.containsKey(EnumSet.of(OSMType.WAY))); + assertTrue(result.get(EnumSet.of(OSMType.WAY)).containsAll(List.of(new OSHDBTag(2,7), new OSHDBTag(1,-1)))); + } + + private void extractIds(String filter, Map, List> expected) { var expression = parser.parse(filter); - var actual = FilterUtil.ids(expression); + var actual = FilterUtil.extractIds(expression); assertEquals(expected.size(), actual.size()); for (var entry : expected.entrySet()) {