# HG changeset patch # User Artem Tikhomirov # Date 1320463278 -3600 # Node ID a37ce7145c3ff3c0b01b644d5b854e0b2d0281f6 # Parent 9747a786a34d997fd0707a5e2505d9cafc976fea Access to repository configuration diff -r 9747a786a34d -r a37ce7145c3f src/org/tmatesoft/hg/internal/ConfigFile.java --- a/src/org/tmatesoft/hg/internal/ConfigFile.java Tue Oct 25 03:30:02 2011 +0200 +++ b/src/org/tmatesoft/hg/internal/ConfigFile.java Sat Nov 05 04:21:18 2011 +0100 @@ -27,7 +27,7 @@ import java.util.Map; /** - * + * .ini / .rc file reader * @author Artem Tikhomirov * @author TMate Software Ltd. */ @@ -44,17 +44,7 @@ } public boolean hasSection(String sectionName) { - return sections == null ? false : sections.indexOf(sectionName) == -1; - } - - // XXX perhaps, should be moved to subclass HgRepoConfig, as it is not common operation for any config file - public boolean hasEnabledExtension(String extensionName) { - int x = sections != null ? sections.indexOf("extensions") : -1; - if (x == -1) { - return false; - } - String value = content.get(x).get(extensionName); - return value != null && !"!".equals(value); + return sections == null ? false : sections.indexOf(sectionName) != -1; } public List getSectionNames() { @@ -92,6 +82,7 @@ // TODO handle %include and %unset directives // TODO "" and lists + // XXX perhaps, single string to keep whole section with substrings for keys/values to minimize number of arrays (String.value) private void read(File f) throws IOException { if (f == null || !f.canRead()) { return; diff -r 9747a786a34d -r a37ce7145c3f src/org/tmatesoft/hg/internal/Filter.java --- a/src/org/tmatesoft/hg/internal/Filter.java Tue Oct 25 03:30:02 2011 +0200 +++ b/src/org/tmatesoft/hg/internal/Filter.java Sat Nov 05 04:21:18 2011 +0100 @@ -33,7 +33,7 @@ ByteBuffer filter(ByteBuffer src); interface Factory { - void initialize(HgRepository hgRepo, ConfigFile cfg); + void initialize(HgRepository hgRepo); // may return null if for a given path and/or options this filter doesn't make any sense Filter create(Path path, Options opts); } diff -r 9747a786a34d -r a37ce7145c3f src/org/tmatesoft/hg/internal/Internals.java --- a/src/org/tmatesoft/hg/internal/Internals.java Tue Oct 25 03:30:02 2011 +0200 +++ b/src/org/tmatesoft/hg/internal/Internals.java Sat Nov 05 04:21:18 2011 +0100 @@ -25,6 +25,7 @@ import java.util.List; import org.tmatesoft.hg.repo.HgInternals; +import org.tmatesoft.hg.repo.HgRepoConfig.ExtensionsSection; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.util.PathRewrite; @@ -73,17 +74,18 @@ } } - public List getFilters(HgRepository hgRepo, ConfigFile cfg) { + public List getFilters(HgRepository hgRepo) { if (filterFactories == null) { filterFactories = new ArrayList(); - if (cfg.hasEnabledExtension("eol")) { + ExtensionsSection cfg = hgRepo.getConfiguration().getExtensions(); + if (cfg.isEnabled("eol")) { NewlineFilter.Factory ff = new NewlineFilter.Factory(); - ff.initialize(hgRepo, cfg); + ff.initialize(hgRepo); filterFactories.add(ff); } - if (cfg.hasEnabledExtension("keyword")) { + if (cfg.isEnabled("keyword")) { KeywordFilter.Factory ff = new KeywordFilter.Factory(); - ff.initialize(hgRepo, cfg); + ff.initialize(hgRepo); filterFactories.add(ff); } } @@ -113,4 +115,13 @@ return System.getProperty("os.name").indexOf("Windows") != -1; } + public ConfigFile readConfiguration(HgRepository hgRepo, File repoRoot) throws IOException { + ConfigFile configFile = new ConfigFile(); + // FIXME use Unix/Win location according to runningOnWindows + configFile.addLocation(new File(System.getProperty("user.home"), ".hgrc")); + // last one, overrides anything else + // /.hg/hgrc + configFile.addLocation(new File(repoRoot, "hgrc")); + return configFile; + } } diff -r 9747a786a34d -r a37ce7145c3f src/org/tmatesoft/hg/internal/KeywordFilter.java --- a/src/org/tmatesoft/hg/internal/KeywordFilter.java Tue Oct 25 03:30:02 2011 +0200 +++ b/src/org/tmatesoft/hg/internal/KeywordFilter.java Sat Nov 05 04:21:18 2011 +0100 @@ -18,11 +18,11 @@ import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Map; import java.util.TreeMap; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.util.Pair; import org.tmatesoft.hg.util.Path; /** @@ -280,12 +280,12 @@ private HgRepository repo; private Path.Matcher matcher; - public void initialize(HgRepository hgRepo, ConfigFile cfg) { + public void initialize(HgRepository hgRepo) { repo = hgRepo; ArrayList patterns = new ArrayList(); - for (Map.Entry e : cfg.getSection("keyword").entrySet()) { - if (!"ignore".equalsIgnoreCase(e.getValue())) { - patterns.add(e.getKey()); + for (Pair e : hgRepo.getConfiguration().getSection("keyword")) { + if (!"ignore".equalsIgnoreCase(e.second())) { + patterns.add(e.first()); } } matcher = new PathGlobMatcher(patterns.toArray(new String[patterns.size()])); diff -r 9747a786a34d -r a37ce7145c3f src/org/tmatesoft/hg/internal/NewlineFilter.java --- a/src/org/tmatesoft/hg/internal/NewlineFilter.java Tue Oct 25 03:30:02 2011 +0200 +++ b/src/org/tmatesoft/hg/internal/NewlineFilter.java Sat Nov 05 04:21:18 2011 +0100 @@ -166,8 +166,8 @@ private String nativeRepoFormat; private String nativeOSFormat; - public void initialize(HgRepository hgRepo, ConfigFile cfg) { - failIfInconsistent = cfg.getBoolean("eol", "only-consistent", true); + public void initialize(HgRepository hgRepo) { + failIfInconsistent = hgRepo.getConfiguration().getBooleanValue("eol", "only-consistent", true); File cfgFile = new File(hgRepo.getWorkingDir(), ".hgeol"); if (!cfgFile.canRead()) { return; diff -r 9747a786a34d -r a37ce7145c3f src/org/tmatesoft/hg/repo/HgInternals.java --- a/src/org/tmatesoft/hg/repo/HgInternals.java Tue Oct 25 03:30:02 2011 +0200 +++ b/src/org/tmatesoft/hg/repo/HgInternals.java Sat Nov 05 04:21:18 2011 +0100 @@ -26,7 +26,6 @@ import java.net.UnknownHostException; import org.tmatesoft.hg.core.SessionContext; -import org.tmatesoft.hg.internal.ConfigFile; import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.internal.RelativePathRewrite; import org.tmatesoft.hg.util.FileIterator; @@ -97,10 +96,6 @@ return repo.getRepositoryRoot(); } - public ConfigFile getRepoConfig() { - return repo.getConfigFile(); - } - public static HgIgnore newHgIgnore(Reader source) throws IOException { HgIgnore hgIgnore = new HgIgnore(); BufferedReader br = source instanceof BufferedReader ? (BufferedReader) source : new BufferedReader(source); @@ -115,7 +110,7 @@ if (hgUser != null && hgUser.trim().length() > 0) { return hgUser.trim(); } - String configValue = getRepoConfig().getString("ui", "username", null); + String configValue = repo.getConfiguration().getStringValue("ui", "username", null); if (configValue != null) { return configValue; } diff -r 9747a786a34d -r a37ce7145c3f src/org/tmatesoft/hg/repo/HgRepoConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/repo/HgRepoConfig.java Sat Nov 05 04:21:18 2011 +0100 @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2011 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.repo; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.tmatesoft.hg.internal.ConfigFile; +import org.tmatesoft.hg.internal.Experimental; +import org.tmatesoft.hg.util.Pair; + +/** + * WORK IN PROGRESS + * + * Repository-specific configuration. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +@Experimental(reason="WORK IN PROGRESS") +public final class HgRepoConfig /*implements RepoChangeListener, perhaps, also RepoChangeNotifier? */{ + /*ease access for inner classes*/ final ConfigFile config; + + /*package-local*/HgRepoConfig(ConfigFile configFile) { + config = configFile; + } + + public Section getSection(String name) { + if (name == null) { + throw new IllegalArgumentException(); + } + if ("paths".equals(name)) { + return new PathsSection(); + } + if ("extensions".equals(name)) { + return new ExtensionsSection(); + } + return new Section(name); + } + + public boolean hasSection(String name) { + return config.hasSection(name); + } + + public boolean getBooleanValue(String section, String key, boolean defaultValue) { + return config.getBoolean(section, key, defaultValue); + } + + public String getStringValue(String section, String key, String defaultValue) { + return config.getString(section, key, defaultValue); + } + + // + // + + public PathsSection getPaths() { + Section s = getSection("paths"); + if (s.exists()) { + return (PathsSection) s; + } + return new PathsSection(); + } + + public ExtensionsSection getExtensions() { + Section s = getSection("extensions"); + if (s.exists()) { + return (ExtensionsSection) s; + } + return new ExtensionsSection(); + } + + /* + * IMPLEMENTATION NOTE: Section is merely a view to configuration file, without any state. + * In case I need to sync access to config (i.e. write) or refresh it later - can be easily done + */ + + public class Section implements Iterable> { + + protected final String section; + + /*package-local*/Section(String sectionName) { + section = sectionName; + } + + public String getName() { + return section; + } + + /** + * Whether this is real section or a bare non-null instance + */ + public boolean exists() { + return hasSection(section); + } + + /** + * @return defined keys, in the order they appear in the section + */ + public List getKeys() { + return new ArrayList(config.getSection(section).keySet()); + } + + /** + * Find out whether key is present and got any value + * @param key identifies an entry to look up + * @return true if key is present in the section and has non-empty value + */ + public boolean isKeySet(String key) { + String value = getStringValue(section, key, null); + return value != null && value.length() > 0; + } + + /** + * Value of a key as boolean + * @param key identifies an entry to look up + * @param defaultValue optional value to return if no entry for the key found in this section + * @return value corresponding to the key, or defaultValue if key not found + */ + public boolean getBoolean(String key, boolean defaultValue) { + return getBooleanValue(section, key, defaultValue); + } + + /** + * Value of a key as regular string + * @param key identifies entry to look up + * @param defaultValue optional value to return if no entry for the key found in this section + * @return value corresponding to the key, or defaultValue if key not found + */ + public String getString(String key, String defaultValue) { + return getStringValue(section, key, defaultValue); + } + + public Iterator> iterator() { + final Map m = config.getSection(section); + if (m.isEmpty()) { + return Collections.>emptyList().iterator(); + } + ArrayList> rv = new ArrayList>(m.size()); + for (Map.Entry e : m.entrySet()) { + rv.add(new Pair(e.getKey(), e.getValue())); + } + return rv.iterator(); + } + } + + /* + * Few well-known sections may get their specific subclasses + */ + + /** + * Section [paths] + */ + public class PathsSection extends Section { + PathsSection() { + super("paths"); + } + + public boolean hasDefault() { + return isKeySet("default"); + } + public String getDefault() { + return super.getString("default", null); + } + public boolean hasDefaultPush() { + return isKeySet("default-push"); + } + public String getDefaultPush() { + return super.getString("default-push", null); + } + } + + /** + * Section [extensions] + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ + public class ExtensionsSection extends Section { + ExtensionsSection() { + super("extensions"); + } + + public boolean isEnabled(String extensionName) { + String value = config.getSection(section).get(extensionName); + return value != null && value.length() > 0 && '!' != value.charAt(0) ; + } + } +} diff -r 9747a786a34d -r a37ce7145c3f src/org/tmatesoft/hg/repo/HgRepository.java --- a/src/org/tmatesoft/hg/repo/HgRepository.java Tue Oct 25 03:30:02 2011 +0200 +++ b/src/org/tmatesoft/hg/repo/HgRepository.java Sat Nov 05 04:21:18 2011 +0100 @@ -73,7 +73,7 @@ private final PathRewrite normalizePath; private final PathRewrite dataPathHelper; private final PathRewrite repoPathHelper; - private final boolean isCaseSensitiveFileSystem; + private final boolean isCaseSensitiveFileSystem; // FIXME keep this inside Internals impl and delegate to Internals all os/fs-specific tasks private final SessionContext sessionContext; private HgChangelog changelog; @@ -88,7 +88,7 @@ private final org.tmatesoft.hg.internal.Internals impl = new org.tmatesoft.hg.internal.Internals(); private HgIgnore ignore; - private ConfigFile configFile; + private HgRepoConfig repoConfig; HgRepository(String repositoryPath) { repoDir = null; @@ -283,6 +283,22 @@ return subRepos.all(); } + + public HgRepoConfig getConfiguration() /* XXX throws HgInvalidControlFileException? Description of the exception suggests it is only for files under ./hg/*/ { + if (repoConfig == null) { + try { + ConfigFile configFile = impl.readConfiguration(this, getRepositoryRoot()); + repoConfig = new HgRepoConfig(configFile); + } catch (IOException ex) { + String m = "Errors while reading user configuration file"; + getContext().getLog().warn(getClass(), ex, m); + return new HgRepoConfig(new ConfigFile()); // empty config, do not cache, allow to try once again + //throw new HgInvalidControlFileException(m, ex, null); + } + } + return repoConfig; + } + // shall be of use only for internal classes /*package-local*/ File getRepositoryRoot() { return repoDir; @@ -354,22 +370,6 @@ return null; // XXX empty stream instead? } - // can't expose internal class, otherwise seems reasonable to have it in API - /*package-local*/ ConfigFile getConfigFile() { - if (configFile == null) { - configFile = new ConfigFile(); - try { - configFile.addLocation(new File(System.getProperty("user.home"), ".hgrc")); - // last one, overrides anything else - // /.hg/hgrc - configFile.addLocation(new File(getRepositoryRoot(), "hgrc")); - } catch (IOException ex) { - getContext().getLog().warn(getClass(), ex, "Errors while reading user configuration file"); - } - } - return configFile; - } - /*package-local*/ List getFiltersFromRepoToWorkingDir(Path p) { return instantiateFilters(p, new Filter.Options(Filter.Direction.FromRepo)); } @@ -387,7 +387,7 @@ } private List instantiateFilters(Path p, Filter.Options opts) { - List factories = impl.getFilters(this, getConfigFile()); + List factories = impl.getFilters(this); if (factories.isEmpty()) { return Collections.emptyList(); } diff -r 9747a786a34d -r a37ce7145c3f test/org/tmatesoft/hg/test/TestAuxUtilities.java --- a/test/org/tmatesoft/hg/test/TestAuxUtilities.java Tue Oct 25 03:30:02 2011 +0200 +++ b/test/org/tmatesoft/hg/test/TestAuxUtilities.java Sat Nov 05 04:21:18 2011 +0100 @@ -22,6 +22,7 @@ import java.nio.ByteBuffer; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.tmatesoft.hg.core.HgCatCommand; import org.tmatesoft.hg.core.Nodeid; @@ -31,11 +32,15 @@ import org.tmatesoft.hg.repo.HgDataFile; import org.tmatesoft.hg.repo.HgManifest; import org.tmatesoft.hg.repo.HgManifest.Flags; +import org.tmatesoft.hg.repo.HgRepoConfig; +import org.tmatesoft.hg.repo.HgRepoConfig.PathsSection; +import org.tmatesoft.hg.repo.HgRepoConfig.Section; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.util.Adaptable; import org.tmatesoft.hg.util.ByteChannel; import org.tmatesoft.hg.util.CancelSupport; import org.tmatesoft.hg.util.CancelledException; +import org.tmatesoft.hg.util.Pair; import org.tmatesoft.hg.util.Path; /** @@ -256,4 +261,30 @@ } }); } + + @Test + @Ignore("just a dump for now, to compare values visually") + public void testRepositoryConfig() throws Exception { + HgRepository repo = Configuration.get().own(); + final HgRepoConfig cfg = repo.getConfiguration(); + Assert.assertNotNull(cfg.getPaths()); + Assert.assertNotNull(cfg.getExtensions()); + final Section dne = cfg.getSection("does-not-exist"); + Assert.assertNotNull(dne); + Assert.assertFalse(dne.exists()); + for (Pair p : cfg.getSection("ui")) { + System.out.printf("%s = %s\n", p.first(), p.second()); + } + final PathsSection p = cfg.getPaths(); + System.out.printf("Known paths: %d. default: %s(%s), default-push: %s(%s)\n", p.getKeys().size(), p.getDefault(), p.hasDefault(), p.getDefaultPush(), p.hasDefaultPush()); + for (String k : cfg.getPaths().getKeys()) { + System.out.println(k); + } + Assert.assertFalse(p.hasDefault() ^ p.getDefault() != null); + Assert.assertFalse(p.hasDefaultPush() ^ p.getDefaultPush() != null); + } + + public static void main(String[] args) throws Exception { + new TestAuxUtilities().testRepositoryConfig(); + } }