@@ -0,0 +1,1238 @@
+package com.peterphi.std.util;%0A%0Aimport org.apache.commons.lang.StringUtils;%0A%0Aimport java.util.Arrays;%0Aimport java.util.Collection;%0Aimport java.util.Objects;%0Aimport java.util.Set;%0Aimport java.util.TreeSet;%0Aimport java.util.stream.Collectors;%0A%0A/**%0A * A case-insensitive case-preserving String Set%0A */%0Apublic class CaseInsensitiveSet extends TreeSet%3CString%3E%0A%7B%0A%09public CaseInsensitiveSet()%0A%09%7B%0A%09%09super(String.CASE_INSENSITIVE_ORDER);%0A%09%7D%0A%0A%0A%09public CaseInsensitiveSet(Collection%3CString%3E existing)%0A%09%7B%0A%09%09this();%0A%0A%09%09if (existing != null)%0A%09%09%09this.addAll(existing);%0A%09%7D%0A%0A%0A%09public static CaseInsensitiveSet of(String... values)%0A%09%7B%0A%09%09return new CaseInsensitiveSet(Set.of(values));%0A%09%7D%0A%0A%0A%09/**%0A%09 * Parse a simple comma-separated representation; leading and trailing whitespace will be stripped from elements; empty items will be ignored%0A%09 *%0A%09 * @param encoded%0A%09 * @return%0A%09 */%0A%09public static CaseInsensitiveSet valueOf(final String encoded)%0A%09%7B%0A%09%09if (StringUtils.isBlank(encoded))%0A%09%09%09return new CaseInsensitiveSet();%0A%09%09else%0A%09%09%09return Arrays%0A%09%09%09%09%09       .stream(StringUtils.split(encoded, ','))%0A%09%09%09%09%09       .map(StringUtils :: trimToNull)%0A%09%09%09%09%09       .filter(Objects :: nonNull)%0A%09%09%09%09%09       .collect(Collectors.toCollection(CaseInsensitiveSet :: new));%0A%09%7D%0A%7D%0A

Add CaseInsensitiveSet, a simple TreeSet<String> that uses a case-insensitive comparator