/* * 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.drill.common.map; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import java.util.Collection; import java.util.Map; import java.util.Set; /** * A special type of {@link Map} with {@link String}s as keys, and the case of a key is ignored for operations involving * keys like {@link #put}, {@link #get}, etc. The keys are stored and retrieved in lower case. Use the static factory * methods to create instances of this class (e.g. {@link #newConcurrentMap}). * * @param <VALUE> the type of values to be stored in the map */ public class CaseInsensitiveMap<VALUE> implements Map<String, VALUE> { /** * Returns a new instance of {@link java.util.concurrent.ConcurrentMap} with key case-insensitivity. See * {@link java.util.concurrent.ConcurrentMap}. * * @param <VALUE> type of values to be stored in the map * @return key case-insensitive concurrent map */ public static <VALUE> CaseInsensitiveMap<VALUE> newConcurrentMap() { return new CaseInsensitiveMap<>(Maps.<String, VALUE>newConcurrentMap()); } /** * Returns a new instance of {@link java.util.HashMap} with key case-insensitivity. See {@link java.util.HashMap}. * * @param <VALUE> type of values to be stored in the map * @return key case-insensitive hash map */ public static <VALUE> CaseInsensitiveMap<VALUE> newHashMap() { return new CaseInsensitiveMap<>(Maps.<String, VALUE>newHashMap()); } /** * Returns a new instance of {@link java.util.HashMap}, with key case-insensitivity, of expected size. * See {@link java.util.HashMap}. * * @param expectedSize expected size * @param <VALUE> type of values to be stored in the map * @return key case-insensitive hash map */ public static <VALUE> CaseInsensitiveMap<VALUE> newHashMapWithExpectedSize(final int expectedSize) { return new CaseInsensitiveMap<>(Maps.<String, VALUE>newHashMapWithExpectedSize(expectedSize)); } /** * Returns a new instance of {@link ImmutableMap} with key case-insensitivity. This map is built from the given * map. See {@link ImmutableMap}. * * @param map map to copy from * @param <VALUE> type of values to be stored in the map * @return key case-insensitive immutable map */ public static <VALUE> CaseInsensitiveMap<VALUE> newImmutableMap(final Map<? extends String, ? extends VALUE> map) { final ImmutableMap.Builder<String, VALUE> builder = ImmutableMap.builder(); for (final Entry<? extends String, ? extends VALUE> entry : map.entrySet()) { builder.put(entry.getKey().toLowerCase(), entry.getValue()); } return new CaseInsensitiveMap<>(builder.build()); } private final Map<String, VALUE> underlyingMap; /** * Use the static factory methods to create instances of this class (e.g. {@link #newConcurrentMap}). * * @param underlyingMap the underlying map */ private CaseInsensitiveMap(final Map<String, VALUE> underlyingMap) { this.underlyingMap = underlyingMap; } @Override public int size() { return underlyingMap.size(); } @Override public boolean isEmpty() { return underlyingMap.isEmpty(); } @Override public boolean containsKey(final Object key) { return key instanceof String && underlyingMap.containsKey(((String) key).toLowerCase()); } @Override public boolean containsValue(final Object value) { return underlyingMap.containsValue(value); } @Override public VALUE get(final Object key) { return key instanceof String ? underlyingMap.get(((String) key).toLowerCase()) : null; } @Override public VALUE put(final String key, final VALUE value) { return underlyingMap.put(key.toLowerCase(), value); } @Override public VALUE remove(final Object key) { return key instanceof String ? underlyingMap.remove(((String) key).toLowerCase()) : null; } @Override public void putAll(final Map<? extends String, ? extends VALUE> map) { for (final Entry<? extends String, ? extends VALUE> entry : map.entrySet()) { underlyingMap.put(entry.getKey().toLowerCase(), entry.getValue()); } } @Override public void clear() { underlyingMap.clear(); } @Override public Set<String> keySet() { return underlyingMap.keySet(); } @Override public Collection<VALUE> values() { return underlyingMap.values(); } @Override public Set<Entry<String, VALUE>> entrySet() { return underlyingMap.entrySet(); } }