Config: Preserve existing case of names in sections
When an application asks for the names in a section, it may want to see the existing case that was stored by the user. For example, Gerrit Code Review wants to store a configuration block like: [access "refs/heads/master"] label-Code-Review = group Developers and although the name label-Code-Review is case-insensitive, it wants to display the case as it appeared in the configuration file. When enumerating section names or variable names (both of which are case-insensitive), Config now keeps track of the string that first appeared, and presents them in file order, permitting applications to use this information. To maintain case-insensitive behavior, the contains() method of the returned Set<String> still performs a case-insensitive compare. This is a behavior change if the caller enumerates the returned Set<String> and copies it to his own Set<String>, and then performs contains() tests against that, as the strings are now the original case from the configuration block. But I don't think anyone actually does this, as the returned sets are immutable and are cached. Change-Id: Ie4e060ef7772958b2062679e462c34c506371740 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
parent
7cd812940d
commit
b2d528887c
|
@ -58,6 +58,7 @@
|
|||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -386,13 +387,25 @@ public void test008_readSectionNames() throws ConfigInvalidException {
|
|||
|
||||
@Test
|
||||
public void test009_readNamesInSection() throws ConfigInvalidException {
|
||||
String configString = "[core]\n" + "repositoryformatversion = 0\n"
|
||||
+ "filemode = false\n" + "logallrefupdates = true\n";
|
||||
String configString = "[core]\n" + "repositoryFormatVersion = 0\n"
|
||||
+ "filemode = false\n" + "logAllRefUpdates = true\n";
|
||||
final Config c = parse(configString);
|
||||
Set<String> names = c.getNames("core");
|
||||
assertEquals("Core section size", 3, names.size());
|
||||
assertTrue("Core section should contain \"filemode\"", names
|
||||
.contains("filemode"));
|
||||
|
||||
assertTrue("Core section should contain \"repositoryFormatVersion\"",
|
||||
names.contains("repositoryFormatVersion"));
|
||||
|
||||
assertTrue("Core section should contain \"repositoryformatversion\"",
|
||||
names.contains("repositoryformatversion"));
|
||||
|
||||
Iterator<String> itr = names.iterator();
|
||||
assertEquals("repositoryFormatVersion", itr.next());
|
||||
assertEquals("filemode", itr.next());
|
||||
assertEquals("logAllRefUpdates", itr.next());
|
||||
assertFalse(itr.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -52,9 +52,12 @@
|
|||
package org.eclipse.jgit.lib;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -1327,39 +1330,71 @@ public boolean equals(Object obj) {
|
|||
}
|
||||
|
||||
public Set<String> parse(Config cfg) {
|
||||
final Set<String> result = new HashSet<String>();
|
||||
final Map<String, String> m = new LinkedHashMap<String, String>();
|
||||
while (cfg != null) {
|
||||
for (final Entry e : cfg.state.get().entryList) {
|
||||
if (e.name != null
|
||||
&& StringUtils.equalsIgnoreCase(e.section, section)) {
|
||||
if (subsection == null && e.subsection == null)
|
||||
result.add(StringUtils.toLowerCase(e.name));
|
||||
else if (e.subsection != null
|
||||
&& e.subsection.equals(subsection))
|
||||
result.add(StringUtils.toLowerCase(e.name));
|
||||
|
||||
if (e.name == null)
|
||||
continue;
|
||||
if (!StringUtils.equalsIgnoreCase(section, e.section))
|
||||
continue;
|
||||
if ((subsection == null && e.subsection == null)
|
||||
|| (subsection != null && subsection
|
||||
.equals(e.subsection))) {
|
||||
String lc = StringUtils.toLowerCase(e.name);
|
||||
if (!m.containsKey(lc))
|
||||
m.put(lc, e.name);
|
||||
}
|
||||
}
|
||||
cfg = cfg.baseConfig;
|
||||
}
|
||||
return Collections.unmodifiableSet(result);
|
||||
return new CaseFoldingSet(m);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SectionNames implements SectionParser<Set<String>> {
|
||||
public Set<String> parse(Config cfg) {
|
||||
final Set<String> result = new HashSet<String>();
|
||||
final Map<String, String> m = new LinkedHashMap<String, String>();
|
||||
while (cfg != null) {
|
||||
for (final Entry e : cfg.state.get().entryList) {
|
||||
if (e.section != null)
|
||||
result.add(StringUtils.toLowerCase(e.section));
|
||||
if (e.section != null) {
|
||||
String lc = StringUtils.toLowerCase(e.section);
|
||||
if (!m.containsKey(lc))
|
||||
m.put(lc, e.section);
|
||||
}
|
||||
}
|
||||
cfg = cfg.baseConfig;
|
||||
}
|
||||
return Collections.unmodifiableSet(result);
|
||||
return new CaseFoldingSet(m);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CaseFoldingSet extends AbstractSet<String> {
|
||||
private final Map<String, String> names;
|
||||
|
||||
CaseFoldingSet(Map<String, String> names) {
|
||||
this.names = Collections.unmodifiableMap(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object needle) {
|
||||
if (!(needle instanceof String))
|
||||
return false;
|
||||
|
||||
String n = (String) needle;
|
||||
return names.containsKey(n)
|
||||
|| names.containsKey(StringUtils.toLowerCase(n));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return names.values().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return names.size();
|
||||
}
|
||||
}
|
||||
|
||||
private static class State {
|
||||
final List<Entry> entryList;
|
||||
|
|
Loading…
Reference in New Issue