org.mule.util.CaseInsensitiveMapWrapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mule-core Show documentation
Show all versions of mule-core Show documentation
Mule server and core classes
/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.util;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Represents a Map from String to {@link T} where the key's case is not taken into account when looking for it, but
* remembered when the key set is retrieved from the map.
* The backing map used will be an instance of the given class passed to the constructor. This allows to make case
* insensitive different kinds of Map implementations, particularly a ConcurrentHashMap, used to achieve high concurrence.
*
* When a key/value pair is put in the map the key case is remembered so when the key set or the entry set is retrieved
* the correct case is returned. This is useful to store, for example, camel case keys. However, two keys that only
* differ in their case will be assumed to be the same key and only one value (the last) will be kept.
* Note: as this map uses a provided class to create the backing map, key rewrite is not ensured. It is possible that
* when redefining a value associated to a key, the key case won't be overwritten and the already existing key case
* will remains in the key set and entry set.
*
* @param The class of the values referenced in the map.
*
* @since 3.6.0
*/
public class CaseInsensitiveMapWrapper implements Map, Serializable
{
private final Map baseMap;
public CaseInsensitiveMapWrapper(Class mapClass, Object... parameters)
{
try
{
baseMap = ClassUtils.instanciateClass(mapClass, parameters);
}
catch (Exception e)
{
throw new RuntimeException(String.format("Can not create an instance of %s", mapClass.getCanonicalName()), e);
}
}
@Override
public int size()
{
return baseMap.size();
}
@Override
public boolean isEmpty()
{
return baseMap.isEmpty();
}
@Override
public boolean containsKey(Object key)
{
return baseMap.containsKey(new CaseInsensitiveMapKey(key));
}
@Override
public boolean containsValue(Object value)
{
return baseMap.containsValue(value);
}
@Override
public T get(Object key)
{
return baseMap.get(new CaseInsensitiveMapKey(key));
}
@Override
public T put(String key, T value)
{
return baseMap.put(new CaseInsensitiveMapKey(key), value);
}
@Override
public T remove(Object key)
{
return baseMap.remove(new CaseInsensitiveMapKey(key));
}
@Override
public void putAll(Map other)
{
if (other instanceof CaseInsensitiveMapWrapper)
{
baseMap.putAll(((CaseInsensitiveMapWrapper) other).baseMap);
}
else
{
for (Map.Entry otherEntry : other.entrySet())
{
put(otherEntry.getKey(), otherEntry.getValue());
}
}
}
@Override
public void clear()
{
baseMap.clear();
}
@Override
public Set keySet()
{
return new KeySet(baseMap.keySet());
}
@Override
public Collection values()
{
return baseMap.values();
}
@Override
public Set> entrySet()
{
return new EntrySet<>(baseMap.entrySet());
}
private static class CaseInsensitiveMapKey implements Serializable
{
private final String key;
private final String keyLowerCase;
private final int keyHash;
public CaseInsensitiveMapKey(Object key)
{
this.key = key.toString();
keyLowerCase = this.key.toLowerCase();
keyHash = keyLowerCase.hashCode();
}
public String getKey()
{
return key;
}
@Override
public int hashCode()
{
return keyHash;
}
@Override
public boolean equals(Object obj)
{
return (obj instanceof CaseInsensitiveMapKey) &&
keyLowerCase.equals(((CaseInsensitiveMapKey) obj).keyLowerCase);
}
}
private static class KeySet extends AbstractConverterSet
{
public KeySet(Set keys)
{
super(keys);
}
@Override
protected Iterator createIterator(Set keys)
{
return new KeyIterator(keys);
}
}
private static class EntrySet extends AbstractConverterSet, Entry>
{
public EntrySet(Set> entries)
{
super(entries);
}
@Override
protected Iterator> createIterator(Set> entries)
{
return new EntryIterator<>(entries);
}
}
private static abstract class AbstractConverterSet extends AbstractSet
{
private final Set aSet;
public AbstractConverterSet(Set set)
{
this.aSet = set;
}
@Override
public int size()
{
return aSet.size();
}
@Override
public Iterator iterator()
{
return createIterator(aSet);
}
protected abstract Iterator createIterator(Set aSet);
}
private static class KeyIterator extends AbstractConverterIterator
{
public KeyIterator(Set keys)
{
super(keys);
}
@Override
protected String convert(CaseInsensitiveMapKey next)
{
return next.getKey();
}
}
private static class EntryIterator extends AbstractConverterIterator, Entry>
{
public EntryIterator(Set> entries)
{
super(entries);
}
@Override
protected Entry convert(Entry next)
{
return new AbstractMap.SimpleEntry<>(next.getKey().getKey(), next.getValue());
}
}
private static abstract class AbstractConverterIterator implements Iterator
{
private final Iterator aIterator;
public AbstractConverterIterator(Set set)
{
aIterator = set.iterator();
}
@Override
public boolean hasNext()
{
return aIterator.hasNext();
}
@Override
public final void remove()
{
aIterator.remove();
}
@Override
public final B next()
{
return convert(aIterator.next());
}
protected abstract B convert(A next);
}
}