Я использую org.apache.commons.lang3.text.StrSubstitutor для синтаксического анализа строки. У меня есть настройка, подобная этому:
StrSubstitutor sub = new StrSubstitutor(messageValues, "&(", ")");
String format = sub.replace("Information: &(killer) killed &(target)!");
Это больше не работает, если я использую ключи в разных случаях:
"Information: &(KILLER) killed &(TARGET)!"
Есть ли способ сделать ключи для String Substitator нечувствительными к регистру?
Я не могу использовать toLowerCase()
потому что я хочу, чтобы ключи были нечувствительны к регистру.
У StrSubstitutor есть конструктор, который принимает экземпляр StrLookup. Вы можете создать реализацию StrLookup, которая уменьшает доступность ключей, прежде чем их искать.
Вот как это выглядит:
public class CaseInsensitiveStrLookup<V> extends StrLookup<V> {
private final Map<String, V> map;
CaseInsensitiveStrLookup(final Map<String, V> map) {
this.map = map;
}
@Override
public String lookup(final String key) {
String lowercaseKey = key.toLowerCase(); //lowercase the key you're looking for
if (map == null) {
return null;
}
final Object obj = map.get(lowercaseKey);
if (obj == null) {
return null;
}
return obj.toString();
}
}
Используя эту реализацию StrLookup, вам не нужно беспокоиться о том, какую карту вы передаете конструктору.
Следующий тестовый пример успешно завершается, используя приведенную выше реализацию:
import org.apache.commons.lang3.text.StrSubstitutor;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.util.HashMap;
import java.util.Map;
@Test
public class TestClass {
@Test
public void test() {
Map<String, String> messageValues = new HashMap<String, String>();
messageValues.put("killer", "Johnson");
messageValues.put("target", "Quagmire");
StrSubstitutor sub = new StrSubstitutor(new CaseInsensitiveStrLookup<String>(messageValues), "&(", ")", '\\');
String format2 = sub.replace("Information: &(killer) killed &(target)!");
String format = sub.replace("Information: &(KILLER) killed &(TARGET)!");
Assert.assertEquals(format, "Information: Johnson killed Quagmire!");
Assert.assertEquals(format2, "Information: Johnson killed Quagmire!");
}
}
StrLookup
- хорошая идея. Имейте в виду, что вам понадобится карта с строчными ключами, чтобы это работало.
Вам не нужно писать пользовательский класс. Предполагая, что вы можете жить с временем доступа к журналу (n), просто используйте TreeMap без учета регистра.
public static void main(String[] args) {
Map<String, String> m = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
m.put("foo", "bar");
StrSubstitutor sub = new StrSubstitutor(m);
String s = sub.replace("${FOO}");
System.out.println(s);
} // prints "bar"
Я думаю, что эта карта без учета регистра будет работать:
import java.util.HashMap;
import java.util.Map;
public class CaseMap<V> extends HashMap<String, V> {
public CaseMap() {
}
public CaseMap(int capacity) {
super(capacity);
}
public CaseMap(int capacity, float loadFactor) {
super(capacity, loadFactor);
}
public CaseMap(Map<String, ? extends V> map) {
putAll(map);
}
public V put(String key, V value) {
return super.put(key.toUpperCase(), value);
}
public V get(Object key) {
if (!(key instanceof String)) return null;
return super.get(((String)key).toUpperCase());
}
}
Если вы не контролируете создание карты messageValues, вы можете создать из нее CaseMap следующим образом:
CaseMap<String> caseFreeMessageValues = new CaseMap<String>(messageValues);
А затем создайте свой StrSubstitutor следующим образом:
StrSubstitutor sub = new StrSubstitutor(messageValues, "&(", ")");
String format = sub.replace("Information: &(KILLER) killed &(TARGET)!");
Возможно, вы захотите подумать о других методах Map
которые также должны быть переопределены, например, containsKey
.
Если вам нужна гибкость, так как Map и токены нечувствительны к регистру, и вы не контролируете построенную карту, вы можете использовать что-то вроде этого.
String replaceTokens(String str, Map<String, String> messageValues) {
if(tokenToValue == null || tokenToValue.size() < 1) return str;
StrSubstitutor caseInsensitiveTokenReplacer = new StrSubstitutor(new CaseInsensitiveStrLookup<>(messageValues),
"&(", ")", '\\');
return caseInsensitiveTokenReplacer.replace(str);
}
Реализация StrLookup
public class CaseInsensitiveStrLookup<V> extends StrLookup<V> {
private final Map<String, V> map = new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER);
public CaseInsensitiveStrLookup(final Map<String, V> map) throws NullValueKeyNotSupported {
if(map.containsKey(null)) throw new Exception(); // Dont want to support null
this.map.putAll(map);
}
@Override
public String lookup(final String key) {
V value = map.get(key);
if(value == null) return null;
return value.toString();
}}
StrSubstitutor
который принимаетStrLookup
и написать свой собственный метод поиска безStrLookup
регистра. Или вы можете создать подклассHashMap<String, String>
который выполняет поиск ключей на карте без учета регистра.