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>