diff --git a/akka-actor/src/main/java/com/typesafe/config/Config.java b/akka-actor/src/main/java/com/typesafe/config/Config.java index 0d83504fe3..1c7fca50e5 100644 --- a/akka-actor/src/main/java/com/typesafe/config/Config.java +++ b/akka-actor/src/main/java/com/typesafe/config/Config.java @@ -87,9 +87,6 @@ public interface Config extends ConfigMergeable { @Override Config withFallback(ConfigMergeable other); - @Override - ConfigObject toValue(); - /** * Returns a replacement config with all substitutions (the * ${foo.bar} syntax, see * If no paths are specified in checkValid()'s parameter list, * validation is for the entire config. - * + * *

* If you specify paths that are not in the reference config, those paths * are ignored. (There's nothing to validate.) @@ -465,7 +462,7 @@ public interface Config extends ConfigMergeable { List getAnyRefList(String path); - List getMemorySizeInBytesList(String path); + List getBytesList(String path); List getMillisecondsList(String path); diff --git a/akka-actor/src/main/java/com/typesafe/config/ConfigException.java b/akka-actor/src/main/java/com/typesafe/config/ConfigException.java index 3973116064..8c23d09533 100644 --- a/akka-actor/src/main/java/com/typesafe/config/ConfigException.java +++ b/akka-actor/src/main/java/com/typesafe/config/ConfigException.java @@ -7,7 +7,7 @@ package com.typesafe.config; /** * All exceptions thrown by the library are subclasses of ConfigException. */ -public class ConfigException extends RuntimeException { +public abstract class ConfigException extends RuntimeException { private static final long serialVersionUID = 1L; final private ConfigOrigin origin; @@ -152,6 +152,11 @@ public class ConfigException extends RuntimeException { } } + /** + * Exception indicating that a path expression was invalid. Try putting + * double quotes around path elements that contain "special" characters. + * + */ public static class BadPath extends ConfigException { private static final long serialVersionUID = 1L; @@ -267,6 +272,11 @@ public class ConfigException extends RuntimeException { } } + /** + * Information about a problem that occurred in {@link Config#checkValid}. A + * {@link ConfigException.ValidationFailed} exception thrown from + * checkValid() includes a list of problems encountered. + */ public static class ValidationProblem { final private String path; @@ -279,19 +289,31 @@ public class ConfigException extends RuntimeException { this.problem = problem; } + /** Returns the config setting causing the problem. */ public String path() { return path; } + /** + * Returns where the problem occurred (origin may include info on the + * file, line number, etc.). + */ public ConfigOrigin origin() { return origin; } + /** Returns a description of the problem. */ public String problem() { return problem; } } + /** + * Exception indicating that {@link Config#checkValid} found validity + * problems. The problems are available via the {@link #problems()} method. + * The getMessage() of this exception is a potentially very + * long string listing all the problems found. + */ public static class ValidationFailed extends ConfigException { private static final long serialVersionUID = 1L; @@ -321,4 +343,20 @@ public class ConfigException extends RuntimeException { return sb.toString(); } } + + /** + * Exception that doesn't fall into any other category. + */ + public static class Generic extends ConfigException { + private static final long serialVersionUID = 1L; + + public Generic(String message, Throwable cause) { + super(message, cause); + } + + public Generic(String message) { + this(message, null); + } + } + } diff --git a/akka-actor/src/main/java/com/typesafe/config/ConfigFactory.java b/akka-actor/src/main/java/com/typesafe/config/ConfigFactory.java index 36ad5c54b4..9251b3fb45 100644 --- a/akka-actor/src/main/java/com/typesafe/config/ConfigFactory.java +++ b/akka-actor/src/main/java/com/typesafe/config/ConfigFactory.java @@ -5,11 +5,13 @@ package com.typesafe.config; import java.io.File; import java.io.Reader; +import java.net.MalformedURLException; import java.net.URL; import java.util.Map; import java.util.Properties; import com.typesafe.config.impl.ConfigImpl; +import com.typesafe.config.impl.ConfigUtil; import com.typesafe.config.impl.Parseable; /** @@ -57,16 +59,6 @@ public final class ConfigFactory { /** * Like {@link #load(String)} but allows you to specify parse and resolve * options. - * - *

- * To be aware of: using - * {@link ConfigResolveOptions#setUseSystemProperties(boolean) - * setUseSystemProperties(false)} with this method has no meaningful effect, - * because the system properties are merged into the config as overrides - * anyway. setUseSystemProperties affects whether to fall back - * to system properties when they are not found in the config, but with - * load(), they will be in the config. - * * @param resourceBasename * the classpath resource name with optional extension * @param parseOptions @@ -100,15 +92,6 @@ public final class ConfigFactory { * Like {@link #load(Config)} but allows you to specify * {@link ConfigResolveOptions}. * - *

- * To be aware of: using - * {@link ConfigResolveOptions#setUseSystemProperties(boolean) - * setUseSystemProperties(false)} with this method has no meaningful effect, - * because the system properties are merged into the config as overrides - * anyway. setUseSystemProperties affects whether to fall back - * to system properties when they are not found in the config, but with - * load(), they will be in the config. - * * @param config * the application's portion of the configuration * @param resolveOptions @@ -121,20 +104,83 @@ public final class ConfigFactory { } private static class DefaultConfigHolder { - static final Config defaultConfig = load("application"); + + private static Config loadDefaultConfig() { + int specified = 0; + + // override application.conf with config.file, config.resource, + // config.url if requested. + String resource = System.getProperty("config.resource"); + if (resource != null) + specified += 1; + String file = System.getProperty("config.file"); + if (file != null) + specified += 1; + String url = System.getProperty("config.url"); + if (url != null) + specified += 1; + + if (specified == 0) { + return load("application"); + } else if (specified > 1) { + throw new ConfigException.Generic("You set more than one of config.file='" + file + + "', config.url='" + url + "', config.resource='" + resource + + "'; don't know which one to use!"); + } else { + if (resource != null) { + // this deliberately does not parseResourcesAnySyntax; if + // people want that they can use an include statement. + return load(parseResources(ConfigFactory.class, resource)); + } else if (file != null) { + return load(parseFile(new File(file))); + } else { + try { + return load(parseURL(new URL(url))); + } catch (MalformedURLException e) { + throw new ConfigException.Generic( + "Bad URL in config.url system property: '" + url + "': " + + e.getMessage(), e); + } + } + } + } + + static final Config defaultConfig = loadDefaultConfig(); } /** * Loads a default configuration, equivalent to {@link #load(String) - * load("application")}. This configuration should be used by libraries and - * frameworks unless an application provides a different one. + * load("application")} in most cases. This configuration should be used by + * libraries and frameworks unless an application provides a different one. *

* This method may return a cached singleton. + *

+ * If the system properties config.resource, + * config.file, or config.url are set, then the + * classpath resource, file, or URL specified in those properties will be + * used rather than the default + * application.{conf,json,properties} classpath resources. + * These system properties should not be set in code (after all, you can + * just parse whatever you want manually and then use {@link #load(Config) + * if you don't want to use application.conf}). The properties + * are intended for use by the person or script launching the application. + * For example someone might have a production.conf that + * include application.conf but then change a couple of values. + * When launching the app they could specify + * -Dconfig.resource=production.conf to get production mode. + *

+ * If no system properties are set to change the location of the default + * configuration, ConfigFactory.load() is equivalent to + * ConfigFactory.load("application"). * * @return configuration for an application */ public static Config load() { - return DefaultConfigHolder.defaultConfig; + try { + return DefaultConfigHolder.defaultConfig; + } catch (ExceptionInInitializerError e) { + throw ConfigUtil.extractInitializerError(e); + } } /** diff --git a/akka-actor/src/main/java/com/typesafe/config/ConfigMergeable.java b/akka-actor/src/main/java/com/typesafe/config/ConfigMergeable.java index 356f89021b..c4280e93ea 100644 --- a/akka-actor/src/main/java/com/typesafe/config/ConfigMergeable.java +++ b/akka-actor/src/main/java/com/typesafe/config/ConfigMergeable.java @@ -17,15 +17,6 @@ package com.typesafe.config; * implementations will break. */ public interface ConfigMergeable { - /** - * Converts this instance to a {@link ConfigValue}. If called on a - * {@code ConfigValue} it returns {@code this}, if called on a - * {@link Config} it's equivalent to {@link Config#root()}. - * - * @return this instance as a {@code ConfigValue} - */ - ConfigValue toValue(); - /** * Returns a new value computed by merging this value with another, with * keys in this value "winning" over the other one. Only diff --git a/akka-actor/src/main/java/com/typesafe/config/ConfigResolveOptions.java b/akka-actor/src/main/java/com/typesafe/config/ConfigResolveOptions.java index 8ed11c4bba..37c7b36d5b 100644 --- a/akka-actor/src/main/java/com/typesafe/config/ConfigResolveOptions.java +++ b/akka-actor/src/main/java/com/typesafe/config/ConfigResolveOptions.java @@ -7,31 +7,24 @@ package com.typesafe.config; * A set of options related to resolving substitutions. Substitutions use the * ${foo.bar} syntax and are documented in the HOCON spec. - * *

* This object is immutable, so the "setters" return a new object. - * *

* Here is an example of creating a custom {@code ConfigResolveOptions}: - * *

  *     ConfigResolveOptions options = ConfigResolveOptions.defaults()
- *         .setUseSystemProperties(false)
  *         .setUseSystemEnvironment(false)
  * 
- * *

* In addition to {@link ConfigResolveOptions#defaults}, there's a prebuilt * {@link ConfigResolveOptions#noSystem} which avoids looking at any system - * properties or environment variables. + * environment variables or other external system information. (Right now, + * environment variables are the only example.) */ public final class ConfigResolveOptions { - private final boolean useSystemProperties; private final boolean useSystemEnvironment; - private ConfigResolveOptions(boolean useSystemProperties, - boolean useSystemEnvironment) { - this.useSystemProperties = useSystemProperties; + private ConfigResolveOptions(boolean useSystemEnvironment) { this.useSystemEnvironment = useSystemEnvironment; } @@ -41,31 +34,17 @@ public final class ConfigResolveOptions { * @return the default resolve options */ public static ConfigResolveOptions defaults() { - return new ConfigResolveOptions(true, true); + return new ConfigResolveOptions(true); } /** * Returns resolve options that disable any reference to "system" data - * (system properties or environment variables). + * (currently, this means environment variables). * - * @return the resolve options with system properties and env variables - * disabled + * @return the resolve options with env variables disabled */ public static ConfigResolveOptions noSystem() { - return defaults().setUseSystemEnvironment(false).setUseSystemProperties(false); - } - - /** - * Returns options with use of Java system properties set to the given - * value. - * - * @param value - * true to resolve substitutions falling back to Java system - * properties. - * @return options with requested setting for use of system properties - */ - public ConfigResolveOptions setUseSystemProperties(boolean value) { - return new ConfigResolveOptions(value, useSystemEnvironment); + return defaults().setUseSystemEnvironment(false); } /** @@ -76,18 +55,9 @@ public final class ConfigResolveOptions { * variables. * @return options with requested setting for use of environment variables */ + @SuppressWarnings("static-method") public ConfigResolveOptions setUseSystemEnvironment(boolean value) { - return new ConfigResolveOptions(useSystemProperties, value); - } - - /** - * Returns whether the options enable use of system properties. This method - * is mostly used by the config lib internally, not by applications. - * - * @return true if system properties should be used - */ - public boolean getUseSystemProperties() { - return useSystemProperties; + return new ConfigResolveOptions(value); } /** diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java b/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java index 3f7cc0edf1..428a7b2b3f 100644 --- a/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java +++ b/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java @@ -35,6 +35,11 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements return config; } + @Override + public AbstractConfigObject toFallbackValue() { + return this; + } + /** * This looks up the key with no transformation or type conversion of any * kind, and returns null if the key is not present. @@ -135,6 +140,7 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements if (ignoresFallbacks()) throw new ConfigException.BugOrBroken("should not be reached"); + boolean changed = false; boolean allResolved = true; Map merged = new HashMap(); Set allKeys = new HashSet(); @@ -150,12 +156,26 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements kept = first; else kept = first.withFallback(second); + merged.put(key, kept); + + if (first != kept) + changed = true; + if (kept.resolveStatus() == ResolveStatus.UNRESOLVED) allResolved = false; } - return new SimpleConfigObject(mergeOrigins(this, fallback), merged, - ResolveStatus.fromBoolean(allResolved), fallback.ignoresFallbacks()); + + ResolveStatus newResolveStatus = ResolveStatus.fromBoolean(allResolved); + boolean newIgnoresFallbacks = fallback.ignoresFallbacks(); + + if (changed) + return new SimpleConfigObject(mergeOrigins(this, fallback), merged, newResolveStatus, + newIgnoresFallbacks); + else if (newResolveStatus != resolveStatus() || newIgnoresFallbacks != ignoresFallbacks()) + return newCopy(newResolveStatus, newIgnoresFallbacks); + else + return this; } @Override diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigValue.java b/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigValue.java index f94cf22123..1bec6ec536 100644 --- a/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigValue.java +++ b/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigValue.java @@ -16,7 +16,7 @@ import com.typesafe.config.ConfigValue; * improperly-factored and non-modular code. Please don't add parent(). * */ -abstract class AbstractConfigValue implements ConfigValue { +abstract class AbstractConfigValue implements ConfigValue, MergeableValue { final private ConfigOrigin origin; @@ -72,7 +72,7 @@ abstract class AbstractConfigValue implements ConfigValue { } @Override - public AbstractConfigValue toValue() { + public AbstractConfigValue toFallbackValue() { return this; } @@ -110,7 +110,7 @@ abstract class AbstractConfigValue implements ConfigValue { if (ignoresFallbacks()) { return this; } else { - ConfigValue other = mergeable.toValue(); + ConfigValue other = ((MergeableValue) mergeable).toFallbackValue(); if (other instanceof Unmergeable) { return mergedWithTheUnmergeable((Unmergeable) other); diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigImpl.java b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigImpl.java index ddb810494c..8c016d6f98 100644 --- a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigImpl.java +++ b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigImpl.java @@ -305,7 +305,11 @@ public class ConfigImpl { } static ConfigIncluder defaultIncluder() { - return DefaultIncluderHolder.defaultIncluder; + try { + return DefaultIncluderHolder.defaultIncluder; + } catch (ExceptionInInitializerError e) { + throw ConfigUtil.extractInitializerError(e); + } } private static AbstractConfigObject loadSystemProperties() { @@ -319,7 +323,11 @@ public class ConfigImpl { } static AbstractConfigObject systemPropertiesAsConfigObject() { - return SystemPropertiesHolder.systemProperties; + try { + return SystemPropertiesHolder.systemProperties; + } catch (ExceptionInInitializerError e) { + throw ConfigUtil.extractInitializerError(e); + } } /** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */ @@ -351,7 +359,11 @@ public class ConfigImpl { } static AbstractConfigObject envVariablesAsConfigObject() { - return EnvVariablesHolder.envVariables; + try { + return EnvVariablesHolder.envVariables; + } catch (ExceptionInInitializerError e) { + throw ConfigUtil.extractInitializerError(e); + } } /** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */ @@ -369,6 +381,10 @@ public class ConfigImpl { /** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */ public static Config defaultReference() { - return ReferenceHolder.referenceConfig; + try { + return ReferenceHolder.referenceConfig; + } catch (ExceptionInInitializerError e) { + throw ConfigUtil.extractInitializerError(e); + } } } diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigSubstitution.java b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigSubstitution.java index 1b27408654..8f1b43571c 100644 --- a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigSubstitution.java +++ b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigSubstitution.java @@ -131,22 +131,25 @@ final class ConfigSubstitution extends AbstractConfigValue implements private ConfigValue resolve(SubstitutionResolver resolver, SubstitutionExpression subst, int depth, ConfigResolveOptions options) { + // First we look up the full path, which means relative to the + // included file if we were not a root file ConfigValue result = findInObject(resolver.root(), resolver, subst.path(), depth, options); - // when looking up system props and env variables, - // we don't want the prefix that was added when - // we were included in another file. - Path unprefixed = subst.path().subPath(prefixLength); + if (result == null) { + // Then we want to check relative to the root file. We don't + // want the prefix we were included at to be used when looking up + // env variables either. + Path unprefixed = subst.path().subPath(prefixLength); - if (result == null && options.getUseSystemProperties()) { - result = findInObject(ConfigImpl.systemPropertiesAsConfigObject(), null, - unprefixed, depth, options); - } + if (result == null && prefixLength > 0) { + result = findInObject(resolver.root(), resolver, unprefixed, depth, options); + } - if (result == null && options.getUseSystemEnvironment()) { - result = findInObject(ConfigImpl.envVariablesAsConfigObject(), null, - unprefixed, depth, options); + if (result == null && options.getUseSystemEnvironment()) { + result = findInObject(ConfigImpl.envVariablesAsConfigObject(), null, unprefixed, + depth, options); + } } return result; diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigUtil.java b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigUtil.java index bfd8f05521..6f7b2c5aaa 100644 --- a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigUtil.java +++ b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigUtil.java @@ -3,6 +3,12 @@ */ package com.typesafe.config.impl; +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; + +import com.typesafe.config.ConfigException; + /** This is public just for the "config" package to use, don't touch it */ final public class ConfigUtil { @@ -118,4 +124,26 @@ final public class ConfigUtil { } return s.substring(start, end); } + + /** This is public just for the "config" package to use, don't touch it! */ + public static ConfigException extractInitializerError(ExceptionInInitializerError e) { + Throwable cause = e.getCause(); + if (cause != null && cause instanceof ConfigException) { + return (ConfigException) cause; + } else { + throw e; + } + } + + static File urlToFile(URL url) { + // this isn't really right, clearly, but not sure what to do. + try { + // this will properly handle hex escapes, etc. + return new File(url.toURI()); + } catch (URISyntaxException e) { + // this handles some stuff like file:///c:/Whatever/ + // apparently but mangles handling of hex escapes + return new File(url.getPath()); + } + } } diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/MergeableValue.java b/akka-actor/src/main/java/com/typesafe/config/impl/MergeableValue.java new file mode 100644 index 0000000000..38dba70cc2 --- /dev/null +++ b/akka-actor/src/main/java/com/typesafe/config/impl/MergeableValue.java @@ -0,0 +1,9 @@ +package com.typesafe.config.impl; + +import com.typesafe.config.ConfigMergeable; +import com.typesafe.config.ConfigValue; + +interface MergeableValue extends ConfigMergeable { + // converts a Config to its root object and a ConfigValue to itself + ConfigValue toFallbackValue(); +} diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/Parseable.java b/akka-actor/src/main/java/com/typesafe/config/impl/Parseable.java index 71808188d3..e5b67540de 100644 --- a/akka-actor/src/main/java/com/typesafe/config/impl/Parseable.java +++ b/akka-actor/src/main/java/com/typesafe/config/impl/Parseable.java @@ -247,6 +247,20 @@ public abstract class Parseable implements ConfigParseable { } } + static File relativeTo(File file, String filename) { + File child = new File(filename); + + if (child.isAbsolute()) + return null; + + File parent = file.getParentFile(); + + if (parent == null) + return null; + else + return new File(parent, filename); + } + private final static class ParseableReader extends Parseable { final private Reader reader; @@ -338,7 +352,13 @@ public abstract class Parseable implements ConfigParseable { } public static Parseable newURL(URL input, ConfigParseOptions options) { - return new ParseableURL(input, options); + // we want file: URLs and files to always behave the same, so switch + // to a file if it's a file: URL + if (input.getProtocol().equals("file")) { + return newFile(ConfigUtil.urlToFile(input), options); + } else { + return new ParseableURL(input, options); + } } private final static class ParseableFile extends Parseable { @@ -362,13 +382,27 @@ public abstract class Parseable implements ConfigParseable { @Override ConfigParseable relativeTo(String filename) { - try { - URL url = relativeTo(input.toURI().toURL(), filename); - if (url == null) - return null; - return newURL(url, options().setOriginDescription(null)); - } catch (MalformedURLException e) { + File sibling; + if ((new File(filename)).isAbsolute()) { + sibling = new File(filename); + } else { + // this may return null + sibling = relativeTo(input, filename); + } + if (sibling == null) return null; + if (sibling.exists()) { + return newFile(sibling, options().setOriginDescription(null)); + } else { + // fall back to classpath; we treat the "filename" as absolute + // (don't add a package name in front), + // if it starts with "/" then remove the "/", for consistency + // with ParseableResources.relativeTo + String resource = filename; + if (filename.startsWith("/")) + resource = filename.substring(1); + return newResources(this.getClass().getClassLoader(), resource, options() + .setOriginDescription(null)); } } @@ -450,6 +484,10 @@ public abstract class Parseable implements ConfigParseable { } static String parent(String resource) { + // the "resource" is not supposed to begin with a "/" + // because it's supposed to be the raw resource + // (ClassLoader#getResource), not the + // resource "syntax" (Class#getResource) int i = resource.lastIndexOf('/'); if (i < 0) { return null; @@ -460,18 +498,24 @@ public abstract class Parseable implements ConfigParseable { @Override ConfigParseable relativeTo(String sibling) { - // here we want to build a new resource name and let - // the class loader have it, rather than getting the - // url with getResource() and relativizing to that url. - // This is needed in case the class loader is going to - // search a classpath. - String parent = parent(resource); - if (parent == null) - return newResources(loader, sibling, options() - .setOriginDescription(null)); - else - return newResources(loader, parent + "/" + sibling, + if (sibling.startsWith("/")) { + // if it starts with "/" then don't make it relative to + // the including resource + return newResources(loader, sibling.substring(1), options().setOriginDescription(null)); + } else { + // here we want to build a new resource name and let + // the class loader have it, rather than getting the + // url with getResource() and relativizing to that url. + // This is needed in case the class loader is going to + // search a classpath. + String parent = parent(resource); + if (parent == null) + return newResources(loader, sibling, options().setOriginDescription(null)); + else + return newResources(loader, parent + "/" + sibling, options() + .setOriginDescription(null)); + } } @Override diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/Path.java b/akka-actor/src/main/java/com/typesafe/config/impl/Path.java index d57cc0091e..f19552c890 100644 --- a/akka-actor/src/main/java/com/typesafe/config/impl/Path.java +++ b/akka-actor/src/main/java/com/typesafe/config/impl/Path.java @@ -139,11 +139,25 @@ final class Path { } // this doesn't have a very precise meaning, just to reduce - // noise from quotes in the rendered path + // noise from quotes in the rendered path for average cases static boolean hasFunkyChars(String s) { - for (int i = 0; i < s.length(); ++i) { + int length = s.length(); + + if (length == 0) + return false; + + // if the path starts with something that could be a number, + // we need to quote it because the number could be invalid, + // for example it could be a hyphen with no digit afterward + // or the exponent "e" notation could be mangled. + char first = s.charAt(0); + if (!(Character.isLetter(first))) + return true; + + for (int i = 1; i < length; ++i) { char c = s.charAt(i); - if (Character.isLetterOrDigit(c) || c == ' ') + + if (Character.isLetterOrDigit(c) || c == '-' || c == '_') continue; else return true; diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/SimpleConfig.java b/akka-actor/src/main/java/com/typesafe/config/impl/SimpleConfig.java index aba696ae65..127a98a05b 100644 --- a/akka-actor/src/main/java/com/typesafe/config/impl/SimpleConfig.java +++ b/akka-actor/src/main/java/com/typesafe/config/impl/SimpleConfig.java @@ -27,9 +27,9 @@ import com.typesafe.config.ConfigValueType; * key-value pairs would be all the tree's leaf values, in a big flat list with * their full paths. */ -class SimpleConfig implements Config { +final class SimpleConfig implements Config, MergeableValue { - AbstractConfigObject object; + final private AbstractConfigObject object; SimpleConfig(AbstractConfigObject object) { this.object = object; @@ -327,7 +327,7 @@ class SimpleConfig implements Config { } @Override - public List getMemorySizeInBytesList(String path) { + public List getBytesList(String path) { List l = new ArrayList(); List list = getList(path); for (ConfigValue v : list) { @@ -378,7 +378,7 @@ class SimpleConfig implements Config { } @Override - public AbstractConfigObject toValue() { + public AbstractConfigObject toFallbackValue() { return object; }