CaseInsensitiveSet.java
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2010–2011 ApexIdentity Inc.
* Portions Copyright 2011-2015 ForgeRock AS.
*/
package org.forgerock.http.util;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* An implementation of a set whose values are case-insensitive strings. All operations match
* values in a case-insensitive manner. The original cases of values are retained, so the
* {@link #iterator() iterator()} method for example returns the originally values.
* <p>
* <strong>Note:</strong> The behavior of this class is undefined when wrapping a set that
* has keys that would result in duplicate case-insensitive values.
*/
public class CaseInsensitiveSet extends SetDecorator<String> {
/** Maps lowercase elements to the real string elements. */
private final Map<String, String> lc;
/**
* Constructs a new empty case-insensitive set. The backing set is a new {@link HashSet}
* with default initial capacity and load factor.
*/
public CaseInsensitiveSet() {
super(new HashSet<String>());
lc = new HashMap<>();
}
/**
* Constructs a new case-insensitive set containing the elements in the specified
* collection. The {@code HashSet} is created with default load factor and an initial
* capacity sufficient to contain the elements in the specified collection.
*
* @param c the collection whose elements are to be placed into this set.
* @throws NullPointerException if the specified collection is {@code null}.
*/
public CaseInsensitiveSet(Collection<String> c) {
super(c instanceof Set ? (Set<String>) c : new HashSet<>(c));
lc = new HashMap<>(c.size());
for (String e : c) {
lc.put(e.toLowerCase(), e);
}
}
/**
* Constructs a new, empty case-insensitive set; the backing {@code HashSet} instance has
* the specified initial capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hash set.
* @param loadFactor the load factor of the hash set.
* @throws IllegalArgumentException if the initial capacity is less than zero, or if the load factor is nonpositive.
*/
public CaseInsensitiveSet(int initialCapacity, float loadFactor) {
super(new HashSet<String>(initialCapacity, loadFactor));
lc = new HashMap<>(initialCapacity, loadFactor);
}
/**
* Constructs a new, empty case-insensitive set; the backing {@code HashSet} instance has
* the specified initial capacity and default load factor.
*
* @param initialCapacity the initial capacity of the hash set.
* @throws IllegalArgumentException if the initial capacity is less than zero.
*/
public CaseInsensitiveSet(int initialCapacity) {
super(new HashSet<String>(initialCapacity));
lc = new HashMap<>(initialCapacity);
}
private Object translate(Object element) {
if (element instanceof String) {
String e = lc.get(((String) element).toLowerCase());
if (e != null) {
// found a mapped-equivalent
element = e;
}
}
return element;
}
@Override
public boolean contains(Object o) {
return super.contains(translate(o));
}
@Override
public boolean add(String e) {
if (contains(e)) {
return false;
}
lc.put(e.toLowerCase(), e);
return super.add(e);
}
@Override
public boolean remove(Object o) {
boolean removed = super.remove(translate(o));
if (o instanceof String) {
lc.remove(((String) o).toLowerCase());
}
return removed;
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object o : c) {
if (!contains(o)) {
return false;
}
}
return true;
}
@Override
public boolean addAll(Collection<? extends String> c) {
boolean changed = false;
for (String e : c) {
if (add(e)) {
changed = true;
}
}
return changed;
}
@Override
public boolean retainAll(Collection<?> c) {
boolean changed = false;
for (String e : this) {
if (!c.contains(e)) {
remove(e);
changed = true;
}
}
return changed;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean changed = false;
for (String e : this) {
if (c.contains(e)) {
remove(e);
changed = true;
}
}
return changed;
}
@Override
public void clear() {
lc.clear();
super.clear();
}
}