You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2018/08/27 19:31:20 UTC
[1/2] calcite git commit: [CALCITE-2481] NameSet assumes lower-case
characters have greater codes, which does not hold for certain characters
Repository: calcite
Updated Branches:
refs/heads/master 6d6421c8a -> 6cad2ee13
[CALCITE-2481] NameSet assumes lower-case characters have greater codes, which does not hold for certain characters
Includes EquivalenceSet, a nice general-purpose class to compute
reflexive, symmetric, transitive closure.
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/6cad2ee1
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/6cad2ee1
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/6cad2ee1
Branch: refs/heads/master
Commit: 6cad2ee13e50e9444f80a744409d4e6ca483a30c
Parents: 8d475f7
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Aug 23 18:04:41 2018 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun Aug 26 21:35:06 2018 -0700
----------------------------------------------------------------------
.../org/apache/calcite/util/EquivalenceSet.java | 142 +++++++++++++
.../org/apache/calcite/util/NameHelper.java | 197 +++++++++++++++++++
.../java/org/apache/calcite/util/NameMap.java | 5 +-
.../org/apache/calcite/util/NameMultimap.java | 14 +-
.../java/org/apache/calcite/util/NameSet.java | 4 +-
.../java/org/apache/calcite/util/UtilTest.java | 175 ++++++++++++++++
6 files changed, 520 insertions(+), 17 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/6cad2ee1/core/src/main/java/org/apache/calcite/util/EquivalenceSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/EquivalenceSet.java b/core/src/main/java/org/apache/calcite/util/EquivalenceSet.java
new file mode 100644
index 0000000..95a4f76
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/util/EquivalenceSet.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.util;
+
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.TreeMultimap;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
+/** Set of elements organized into equivalence classes.
+ *
+ * <p>Elements are equivalent by the rules of a mathematical equivalence
+ * relation:
+ *
+ * <dl>
+ * <dt>Reflexive
+ * <dd>Every element {@code e} is equivalent to itself
+ * <dt>Symmetric
+ * <dd>If {@code e} is equivalent to {@code f},
+ * then {@code f} is equivalent to {@code e}
+ * <dt>Transitive
+ * <dd>If {@code e} is equivalent to {@code f},
+ * and {@code f} is equivalent to {@code g},
+ * then {@code e} is equivalent to {@code g}
+ * </dl>
+ *
+ * <p>For any given pair of elements, answers in O(log N) (two hash-table
+ * lookups) whether they are equivalent to each other.
+ *
+ * @param <E> Element type
+ */
+public class EquivalenceSet<E extends Comparable<E>> {
+ private final Map<E, E> parents = new HashMap<>();
+
+ /** Adds an element, and returns the element (which is its own parent).
+ * If already present, returns the element's parent. */
+ public E add(E e) {
+ final E parent = parents.get(Objects.requireNonNull(e));
+ if (parent == null) {
+ // Element is new. Add it to the map, as its own parent.
+ parents.put(e, e);
+ return e;
+ } else {
+ return parent;
+ }
+ }
+
+ /** Marks two elements as equivalent.
+ * They may or may not be registered, and they may or may not be equal. */
+ public E equiv(E e, E f) {
+ final E eParent = add(e);
+ if (!eParent.equals(e)) {
+ assert parents.get(eParent).equals(eParent);
+ final E root = equiv(eParent, f);
+ parents.put(e, root);
+ return root;
+ }
+ final E fParent = add(f);
+ if (!fParent.equals(f)) {
+ assert parents.get(fParent).equals(fParent);
+ final E root = equiv(e, fParent);
+ parents.put(f, root);
+ return root;
+ }
+ final int c = e.compareTo(f);
+ if (c == 0) {
+ return e;
+ }
+ if (c < 0) {
+ // e is a better (lower) parent of f
+ parents.put(f, e);
+ return e;
+ } else {
+ // f is a better (lower) parent of e
+ parents.put(e, f);
+ return f;
+ }
+ }
+
+ /** Returns whether two elements are in the same equivalence class.
+ * Returns false if either or both of the elements are not registered. */
+ public boolean areEquivalent(E e, E f) {
+ final E eParent = parents.get(e);
+ final E fParent = parents.get(f);
+ return Objects.equals(eParent, fParent);
+ }
+
+ /** Returns a map of the canonical element in each equivalence class to the
+ * set of elements in that class. The keys are sorted in natural order, as
+ * are the elements within each key. */
+ public SortedMap<E, SortedSet<E>> map() {
+ final TreeMultimap<E, E> multimap = TreeMultimap.create();
+ for (Map.Entry<E, E> entry : parents.entrySet()) {
+ multimap.put(entry.getValue(), entry.getKey());
+ }
+ // Create an immutable copy. Keys and values remain in sorted order.
+ final ImmutableSortedMap.Builder<E, SortedSet<E>> builder =
+ ImmutableSortedMap.naturalOrder();
+ for (Map.Entry<E, Collection<E>> entry : multimap.asMap().entrySet()) {
+ builder.put(entry.getKey(), ImmutableSortedSet.copyOf(entry.getValue()));
+ }
+ return builder.build();
+ }
+
+ /** Removes all elements in this equivalence set. */
+ public void clear() {
+ parents.clear();
+ }
+
+ /** Returns the number of elements in this equivalence set. */
+ public int size() {
+ return parents.size();
+ }
+
+ /** Returns the number of equivalence classes in this equivalence set. */
+ public int classCount() {
+ return new HashSet<>(parents.values()).size();
+ }
+}
+
+// End EquivalenceSet.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/6cad2ee1/core/src/main/java/org/apache/calcite/util/NameHelper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/NameHelper.java b/core/src/main/java/org/apache/calcite/util/NameHelper.java
new file mode 100644
index 0000000..bba80ac
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/util/NameHelper.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.util;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Ordering;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+
+import static org.apache.calcite.util.NameSet.COMPARATOR;
+
+/** Helps construct case-insensitive ranges of {@link NameSet},
+ * {@link NameMap}, {@link NameMultimap}.
+ *
+ * <p>Not thread-safe. */
+class NameHelper {
+ /** Characters whose floor/ceiling are not the same as their upper/lower
+ * case. Out of 64k unicode characters, there are 289 weird characters. */
+ private static final ImmutableMap<Character, Pair<Character, Character>>
+ WEIRD_CHARACTERS = weirdCharacters();
+
+ /** Workspace for computing the floor key. */
+ private final StringBuilder floorBuilder = new StringBuilder();
+
+ /** Workspace for computing the ceiling key. */
+ private final StringBuilder ceilingBuilder = new StringBuilder();
+
+ /** Given a string, computes the smallest and largest strings that are
+ * case-insensitive equal to that string,
+ * calls the given function,
+ * and returns its result.
+ *
+ * <p>For latin strings such as "bAz" computing the smallest and largest
+ * strings is straightforward:
+ * the floor is the upper-case string ("BAZ"), and
+ * the ceiling is the lower-case string ("baz").
+ *
+ * <p>It's more complicated for non-Latin strings that have characters
+ * whose lower-case value is less than their upper-case value.
+ *
+ * <p>This method is not thread-safe.
+ */
+ private <R> R applyFloorCeiling(String name,
+ BiFunction<String, String, R> f) {
+ name.chars()
+ .forEachOrdered(i -> {
+ final char c = (char) i;
+ final Pair<Character, Character> pair = WEIRD_CHARACTERS.get(c);
+ if (pair == null) {
+ floorBuilder.append(Character.toUpperCase(c));
+ ceilingBuilder.append(Character.toLowerCase(c));
+ } else {
+ floorBuilder.append(pair.left);
+ ceilingBuilder.append(pair.right);
+ }
+ });
+ final String floor = bufValue(floorBuilder, name);
+ final String ceiling = bufValue(ceilingBuilder, name);
+ assert floor.compareTo(ceiling) <= 0;
+ return f.apply(floor, ceiling);
+ }
+
+ /** Returns the value of a {@link StringBuilder} as a string,
+ * and clears the builder.
+ *
+ * <p>If the value is the same the given string, returns that string,
+ * thereby saving the effort of building a new string. */
+ private static String bufValue(StringBuilder b, String s) {
+ if (b.length() != s.length() || b.indexOf(s) != 0) {
+ s = b.toString();
+ }
+ b.setLength(0);
+ return s;
+ }
+
+ /** Used by {@link NameSet#range(String, boolean)}. */
+ Collection<String> set(NavigableSet<String> names, String name) {
+ return applyFloorCeiling(name,
+ (floor, ceiling) -> {
+ final NavigableSet<String> subSet =
+ names.subSet(floor, true, ceiling, true);
+ return subSet
+ .stream()
+ .filter(s -> s.equalsIgnoreCase(name))
+ .collect(Collectors.toList());
+ });
+ }
+
+ /** Used by {@link NameMap#range(String, boolean)}. */
+ <V> ImmutableSortedMap<String, V> map(NavigableMap<String, V> map,
+ String name) {
+ return applyFloorCeiling(name,
+ (floor, ceiling) -> {
+ final ImmutableSortedMap.Builder<String, V> builder =
+ new ImmutableSortedMap.Builder<>(COMPARATOR);
+ final NavigableMap<String, V> subMap =
+ map.subMap(floor, true, ceiling, true);
+ for (Map.Entry<String, V> e : subMap.entrySet()) {
+ if (e.getKey().equalsIgnoreCase(name)) {
+ builder.put(e.getKey(), e.getValue());
+ }
+ }
+ return builder.build();
+ });
+ }
+
+ /** Used by {@link NameMultimap#range(String, boolean)}. */
+ <V> Collection<Map.Entry<String, V>> multimap(
+ NavigableMap<String, List<V>> map, String name) {
+ return applyFloorCeiling(name,
+ (floor, ceiling) -> {
+ final NavigableMap<String, List<V>> subMap =
+ map.subMap(floor, true, ceiling, true);
+ final ImmutableList.Builder<Map.Entry<String, V>> builder =
+ ImmutableList.builder();
+ for (Map.Entry<String, List<V>> e : subMap.entrySet()) {
+ if (e.getKey().equalsIgnoreCase(name)) {
+ for (V v : e.getValue()) {
+ builder.add(Pair.of(e.getKey(), v));
+ }
+ }
+ }
+ return builder.build();
+ });
+ }
+
+ /** Returns whether an equivalence class of characters is simple.
+ *
+ * <p>It is simple if
+ * the floor of the class is the upper-case value of every character, and
+ * the ceiling of the class is the lower-case value of every character. */
+ private static boolean isSimple(Collection<Character> characters,
+ Character floor, Character ceiling) {
+ for (Character character : characters) {
+ if (!floor.equals(Character.toUpperCase(character))) {
+ return false;
+ }
+ if (!ceiling.equals(Character.toLowerCase(character))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static ImmutableMap<Character, Pair<Character, Character>>
+ weirdCharacters() {
+ final EquivalenceSet<Character> strange = new EquivalenceSet<>();
+ for (int i = 0; i < 0xffff; i++) {
+ char c = (char) i;
+ strange.add(c);
+ strange.equiv(c, Character.toLowerCase(c));
+ strange.equiv(c, Character.toUpperCase(c));
+ }
+ final SortedMap<Character, SortedSet<Character>> map = strange.map();
+ final ImmutableMap.Builder<Character, Pair<Character, Character>> builder =
+ ImmutableMap.builder();
+ for (Map.Entry<Character, SortedSet<Character>> entry : map.entrySet()) {
+ final Collection<Character> characters = entry.getValue();
+ final Character floor = Ordering.natural().min(characters);
+ final Character ceiling = Ordering.natural().max(characters);
+ if (isSimple(characters, floor, ceiling)) {
+ continue;
+ }
+ final Pair<Character, Character> pair = Pair.of(floor, ceiling);
+ for (Character character : characters) {
+ builder.put(character, pair);
+ }
+ }
+ return builder.build();
+ }
+}
+
+// End NameHelper.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/6cad2ee1/core/src/main/java/org/apache/calcite/util/NameMap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/NameMap.java b/core/src/main/java/org/apache/calcite/util/NameMap.java
index cd138ea..e48d63f 100644
--- a/core/src/main/java/org/apache/calcite/util/NameMap.java
+++ b/core/src/main/java/org/apache/calcite/util/NameMap.java
@@ -20,7 +20,6 @@ import org.apache.calcite.linq4j.function.Experimental;
import com.google.common.collect.ImmutableSortedMap;
-import java.util.Locale;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
@@ -33,6 +32,7 @@ import static org.apache.calcite.util.NameSet.COMPARATOR;
* @param <V> Value type */
public class NameMap<V> {
private final NavigableMap<String, V> map;
+ private final NameHelper helper = new NameHelper();
/** Creates a NameSet based on an existing set. */
private NameMap(NavigableMap<String, V> map) {
@@ -79,8 +79,7 @@ public class NameMap<V> {
return ImmutableSortedMap.of();
}
} else {
- return map.subMap(name.toUpperCase(Locale.ROOT), true,
- name.toLowerCase(Locale.ROOT), true);
+ return helper.map(map, name);
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/6cad2ee1/core/src/main/java/org/apache/calcite/util/NameMultimap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/NameMultimap.java b/core/src/main/java/org/apache/calcite/util/NameMultimap.java
index a535809..5e51818 100644
--- a/core/src/main/java/org/apache/calcite/util/NameMultimap.java
+++ b/core/src/main/java/org/apache/calcite/util/NameMultimap.java
@@ -23,7 +23,6 @@ import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
@@ -36,6 +35,7 @@ import static org.apache.calcite.util.NameSet.COMPARATOR;
* @param <V> Value type */
public class NameMultimap<V> {
private final NavigableMap<String, List<V>> map;
+ private final NameHelper helper = new NameHelper();
/** Creates a NameMultimap based on an existing map. */
private NameMultimap(NavigableMap<String, List<V>> map) {
@@ -97,17 +97,7 @@ public class NameMultimap<V> {
return ImmutableList.of();
}
} else {
- final ImmutableList.Builder<Map.Entry<String, V>> builder =
- ImmutableList.builder();
- NavigableMap<String, List<V>> m =
- map.subMap(name.toUpperCase(Locale.ROOT), true,
- name.toLowerCase(Locale.ROOT), true);
- for (Map.Entry<String, List<V>> entry : m.entrySet()) {
- for (V v : entry.getValue()) {
- builder.add(Pair.of(entry.getKey(), v));
- }
- }
- return builder.build();
+ return helper.multimap(map, name);
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/6cad2ee1/core/src/main/java/org/apache/calcite/util/NameSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/NameSet.java b/core/src/main/java/org/apache/calcite/util/NameSet.java
index 3f78d18..12a291e 100644
--- a/core/src/main/java/org/apache/calcite/util/NameSet.java
+++ b/core/src/main/java/org/apache/calcite/util/NameSet.java
@@ -43,6 +43,7 @@ public class NameSet {
};
private final NavigableSet<String> names;
+ private final NameHelper helper = new NameHelper();
/** Creates a NameSet based on an existing set. */
private NameSet(NavigableSet<String> names) {
@@ -89,8 +90,7 @@ public class NameSet {
return ImmutableList.of();
}
} else {
- return names.subSet(name.toUpperCase(Locale.ROOT), true,
- name.toLowerCase(Locale.ROOT), true);
+ return helper.set(names, name);
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/6cad2ee1/core/src/test/java/org/apache/calcite/util/UtilTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/util/UtilTest.java b/core/src/test/java/org/apache/calcite/util/UtilTest.java
index 5c21892..a4070b2 100644
--- a/core/src/test/java/org/apache/calcite/util/UtilTest.java
+++ b/core/src/test/java/org/apache/calcite/util/UtilTest.java
@@ -74,12 +74,14 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.Properties;
import java.util.Random;
import java.util.RandomAccess;
import java.util.Set;
+import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TimeZone;
import java.util.TreeSet;
@@ -2024,6 +2026,141 @@ public class UtilTest {
assertThat(names.contains("WOMBAT", false), is(true));
assertThat(names.contains("zyMurgy", true), is(false));
assertThat(names.contains("zyMurgy", false), is(true));
+
+ // [CALCITE-2481] NameSet assumes lowercase characters have greater codes
+ // which does not hold for certain characters
+ checkCase0("a");
+ checkCase0("\u00b5"); // "ยต"
+ }
+
+ private void checkCase0(String s) {
+ checkCase1(s);
+ checkCase1(s.toUpperCase(Locale.ROOT));
+ checkCase1(s.toLowerCase(Locale.ROOT));
+ checkCase1("a" + s + "z");
+ }
+
+ private void checkCase1(String s) {
+ final NameSet set = new NameSet();
+ set.add(s);
+ checkNameSet(s, set);
+
+ set.add("");
+ checkNameSet(s, set);
+
+ set.add("zzz");
+ checkNameSet(s, set);
+
+ final NameMap<Integer> map = new NameMap<>();
+ map.put(s, 1);
+ checkNameMap(s, map);
+
+ map.put("", 11);
+ checkNameMap(s, map);
+
+ map.put("zzz", 21);
+ checkNameMap(s, map);
+
+ final NameMultimap<Integer> multimap = new NameMultimap<>();
+ multimap.put(s, 1);
+ checkNameMultimap(s, multimap);
+
+ multimap.put("", 11);
+ checkNameMultimap(s, multimap);
+
+ multimap.put("zzz", 21);
+ checkNameMultimap(s, multimap);
+ }
+
+ private void checkNameSet(String s, NameSet set) {
+ final String upper = s.toUpperCase(Locale.ROOT);
+ final String lower = s.toLowerCase(Locale.ROOT);
+ final boolean isUpper = upper.equals(s);
+ final boolean isLower = lower.equals(s);
+ assertThat(set.contains(s, true), is(true));
+ assertThat(set.contains(s, false), is(true));
+ assertThat(set.contains(upper, false), is(true));
+ assertThat(set.contains(upper, true), is(isUpper));
+ assertThat(set.contains(lower, false), is(true));
+ assertThat(set.contains(lower, true), is(isLower));
+
+ // Create a copy of NameSet, to avoid polluting further tests
+ final NameSet set2 = new NameSet();
+ for (String name : set.iterable()) {
+ set2.add(name);
+ }
+ set2.add(upper);
+ set2.add(lower);
+ final Collection<String> rangeInsensitive = set2.range(s, false);
+ assertThat(rangeInsensitive.contains(s), is(true));
+ assertThat(rangeInsensitive.contains(upper), is(true));
+ assertThat(rangeInsensitive.contains(lower), is(true));
+ final Collection<String> rangeSensitive = set2.range(s, true);
+ assertThat(rangeSensitive.contains(s), is(true));
+ assertThat(rangeSensitive.contains(upper), is(isUpper));
+ assertThat(rangeSensitive.contains(lower), is(isLower));
+ }
+
+ private void checkNameMap(String s, NameMap<Integer> map) {
+ final String upper = s.toUpperCase(Locale.ROOT);
+ final String lower = s.toLowerCase(Locale.ROOT);
+ boolean isUpper = upper.equals(s);
+ boolean isLower = lower.equals(s);
+ assertThat(map.containsKey(s, true), is(true));
+ assertThat(map.containsKey(s, false), is(true));
+ assertThat(map.containsKey(upper, false), is(true));
+ assertThat(map.containsKey(upper, true), is(isUpper));
+ assertThat(map.containsKey(lower, false), is(true));
+ assertThat(map.containsKey(lower, true), is(isLower));
+
+ // Create a copy of NameMap, to avoid polluting further tests
+ final NameMap<Integer> map2 = new NameMap<>();
+ for (Map.Entry<String, Integer> entry : map.map().entrySet()) {
+ map2.put(entry.getKey(), entry.getValue());
+ }
+ map2.put(upper, 2);
+ map2.put(lower, 3);
+ final NavigableMap<String, Integer> rangeInsensitive =
+ map2.range(s, false);
+ assertThat(rangeInsensitive.containsKey(s), is(true));
+ assertThat(rangeInsensitive.containsKey(upper), is(true));
+ assertThat(rangeInsensitive.containsKey(lower), is(true));
+ final NavigableMap<String, Integer> rangeSensitive = map2.range(s, true);
+ assertThat(rangeSensitive.containsKey(s), is(true));
+ assertThat(rangeSensitive.containsKey(upper), is(isUpper));
+ assertThat(rangeSensitive.containsKey(lower), is(isLower));
+ }
+
+ private void checkNameMultimap(String s, NameMultimap<Integer> map) {
+ final String upper = s.toUpperCase(Locale.ROOT);
+ final String lower = s.toLowerCase(Locale.ROOT);
+ boolean isUpper = upper.equals(s);
+ boolean isLower = lower.equals(s);
+ assertThat(map.containsKey(s, true), is(true));
+ assertThat(map.containsKey(s, false), is(true));
+ assertThat(map.containsKey(upper, false), is(true));
+ assertThat(map.containsKey(upper, true), is(isUpper));
+ assertThat(map.containsKey(lower, false), is(true));
+ assertThat(map.containsKey(lower, true), is(isLower));
+
+ // Create a copy of NameMultimap, to avoid polluting further tests
+ final NameMap<Integer> map2 = new NameMap<>();
+ for (Map.Entry<String, List<Integer>> entry : map.map().entrySet()) {
+ for (Integer integer : entry.getValue()) {
+ map2.put(entry.getKey(), integer);
+ }
+ }
+ map2.put(upper, 2);
+ map2.put(lower, 3);
+ final NavigableMap<String, Integer> rangeInsensitive =
+ map2.range(s, false);
+ assertThat(rangeInsensitive.containsKey(s), is(true));
+ assertThat(rangeInsensitive.containsKey(upper), is(true));
+ assertThat(rangeInsensitive.containsKey(lower), is(true));
+ final NavigableMap<String, Integer> rangeSensitive = map2.range(s, true);
+ assertThat(rangeSensitive.containsKey(s), is(true));
+ assertThat(rangeSensitive.containsKey(upper), is(isUpper));
+ assertThat(rangeSensitive.containsKey(lower), is(isLower));
}
/** Unit test for {@link org.apache.calcite.util.NameMap}. */
@@ -2251,6 +2388,44 @@ public class UtilTest {
isIterable(Arrays.asList("John", "Paul", "Ringo")));
}
+ @Test public void testEquivalenceSet() {
+ final EquivalenceSet<String> c = new EquivalenceSet<>();
+ assertThat(c.size(), is(0));
+ assertThat(c.classCount(), is(0));
+ c.add("abc");
+ assertThat(c.size(), is(1));
+ assertThat(c.classCount(), is(1));
+ c.add("Abc");
+ assertThat(c.size(), is(2));
+ assertThat(c.classCount(), is(2));
+ assertThat(c.areEquivalent("abc", "Abc"), is(false));
+ assertThat(c.areEquivalent("abc", "abc"), is(true));
+ assertThat(c.areEquivalent("abc", "ABC"), is(false));
+ c.equiv("abc", "ABC");
+ assertThat(c.size(), is(3));
+ assertThat(c.classCount(), is(2));
+ assertThat(c.areEquivalent("abc", "ABC"), is(true));
+ assertThat(c.areEquivalent("ABC", "abc"), is(true));
+ assertThat(c.areEquivalent("abc", "abc"), is(true));
+ assertThat(c.areEquivalent("abc", "Abc"), is(false));
+ c.equiv("Abc", "ABC");
+ assertThat(c.size(), is(3));
+ assertThat(c.classCount(), is(1));
+ assertThat(c.areEquivalent("abc", "Abc"), is(true));
+
+ c.add("de");
+ c.equiv("fg", "fG");
+ assertThat(c.size(), is(6));
+ assertThat(c.classCount(), is(3));
+ final SortedMap<String, SortedSet<String>> map = c.map();
+ assertThat(map.toString(),
+ is("{ABC=[ABC, Abc, abc], de=[de], fG=[fG, fg]}"));
+
+ c.clear();
+ assertThat(c.size(), is(0));
+ assertThat(c.classCount(), is(0));
+ }
+
private static <E> Matcher<Iterable<E>> isIterable(final Iterable<E> iterable) {
final List<E> list = toList(iterable);
return new TypeSafeMatcher<Iterable<E>>() {
[2/2] calcite git commit: [CALCITE-2467] Upgrade
owasp-dependency-check maven plugin to 3.3.1
Posted by jh...@apache.org.
[CALCITE-2467] Upgrade owasp-dependency-check maven plugin to 3.3.1
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/8d475f76
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/8d475f76
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/8d475f76
Branch: refs/heads/master
Commit: 8d475f769a4cc01644826cedea818f503cfd7660
Parents: 6d6421c
Author: Julian Hyde <jh...@apache.org>
Authored: Wed Aug 15 11:09:54 2018 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun Aug 26 21:35:06 2018 -0700
----------------------------------------------------------------------
pom.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/8d475f76/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 326aa9c..3ce9922 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,7 +78,7 @@ limitations under the License.
<forbiddenapis.version>2.5</forbiddenapis.version>
<freemarker.version>2.3.25-incubating</freemarker.version>
<git-commit-id-plugin.version>2.1.9</git-commit-id-plugin.version>
- <geode.version>1.3.0</geode.version>
+ <geode.version>1.6.0</geode.version>
<fongo.version>2.1.1</fongo.version>
<foodmart-data-json.version>0.4</foodmart-data-json.version>
@@ -96,7 +96,7 @@ limitations under the License.
<hydromatic-resource.version>0.6</hydromatic-resource.version>
<hydromatic-toolbox.version>0.3</hydromatic-toolbox.version>
<hydromatic-tpcds.version>0.4</hydromatic-tpcds.version>
- <jackson.version>2.9.4</jackson.version>
+ <jackson.version>2.9.6</jackson.version>
<janino.version>2.7.6</janino.version>
<java-diff.version>1.1.1</java-diff.version>
<javacc-maven-plugin.version>2.4</javacc-maven-plugin.version>
@@ -120,7 +120,7 @@ limitations under the License.
<natty.version>0.13</natty.version>
<opencsv.version>2.3</opencsv.version>
<oracle-jdbc6-driver.version>11.2.0.2.0</oracle-jdbc6-driver.version>
- <owasp-dependency-check.version>2.1.1</owasp-dependency-check.version>
+ <owasp-dependency-check.version>3.3.1</owasp-dependency-check.version>
<pig.version>0.16.0</pig.version>
<aggdesigner.version>6.0</aggdesigner.version>
<postgresql.version>9.3-1102-jdbc3</postgresql.version>