package com.bergerkiller.bukkit.common.collections; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import com.bergerkiller.bukkit.common.conversion.BasicConverter; import com.bergerkiller.bukkit.common.conversion.ConversionPairs; import com.bergerkiller.bukkit.common.conversion.Converter; import com.bergerkiller.bukkit.common.conversion.ConverterPair; import com.bergerkiller.bukkit.common.conversion.util.ConvertingEntrySet; import com.bergerkiller.bukkit.common.conversion.util.ConvertingSet; /** * A high-performance HashMap implementation that ignores the case of keys. The keys are stored in the original case. * This map violates it general contract for that reason, as keys no longer have to equal one another. * * @param <V> - Value type to map to String keys */ public class StringMapCaseInsensitive<V> implements Map<String, V> { private static final Converter<StringWrap> toStringWrap = new BasicConverter<StringWrap>(StringWrap.class) { @Override protected StringWrap convertSpecial(Object value, Class<?> valueType, StringWrap def) { if (value instanceof String) { return new StringWrap((String) value); } else { return def; } } }; private static final Converter<String> toString = new BasicConverter<String>(String.class) { @Override protected String convertSpecial(Object value, Class<?> valueType, String def) { if (value instanceof StringWrap) { return ((StringWrap) value).key; } else { return def; } } @Override public boolean isRegisterSupported() { return false; } }; private static final ConverterPair<StringWrap, String> pair = toStringWrap.formPair(toString); private final HashMap<StringWrap, V> base = new HashMap<StringWrap, V>(); private final StringWrap tmpWrap = new StringWrap(""); @Override public int size() { return base.size(); } @Override public boolean isEmpty() { return base.isEmpty(); } @Override public boolean containsKey(Object key) { if (key instanceof String) { return base.containsKey(tmpWrap.fill((String) key)); } else if (key == null) { return base.containsKey(key); } else { return false; } } @Override public boolean containsValue(Object value) { return base.containsValue(value); } @Override public V get(Object key) { if (key instanceof String) { return base.get(tmpWrap.fill((String) key)); } else if (key == null) { return base.get(null); } else { return null; } } @Override public V put(String key, V value) { return base.put(key == null ? null : new StringWrap(key), value); } @Override public V remove(Object key) { if (key == null) { return base.remove(key); } else if (key instanceof String) { return base.remove(tmpWrap.fill((String) key)); } else { return null; } } @Override public void clear() { base.clear(); } @Override public void putAll(Map<? extends String, ? extends V> m) { for (Entry<? extends String, ? extends V> entry : m.entrySet()) { put(entry.getKey(), entry.getValue()); } } @Override public Set<String> keySet() { return new ConvertingSet<String>(base.keySet(), pair); } @Override public Collection<V> values() { return base.values(); } @Override @SuppressWarnings("unchecked") public Set<Entry<String, V>> entrySet() { return new ConvertingEntrySet<String, V>(base.entrySet(), pair, ConversionPairs.NONE); } private static class StringWrap { private String key; private int hashCode; public StringWrap(String key) { fill(key); } public StringWrap fill(String key) { this.key = key; this.hashCode = 0; for (int i = 0; i < key.length(); i++) { this.hashCode = 31 * this.hashCode + Character.toLowerCase(key.charAt(i)); } return this; } @Override public boolean equals(Object o) { return o instanceof StringWrap && this.key.equalsIgnoreCase(((StringWrap) o).key); } @Override public int hashCode() { return this.hashCode; } } }