001/**
002 *   Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
003 */
004package com.typesafe.config;
005
006/**
007 * A set of options related to resolving substitutions. Substitutions use the
008 * <code>${foo.bar}</code> syntax and are documented in the <a
009 * href="https://github.com/lightbend/config/blob/master/HOCON.md">HOCON</a>
010 * spec.
011 * <p>
012 * Typically this class would be used with the method
013 * {@link Config#resolve(ConfigResolveOptions)}.
014 * <p>
015 * This object is immutable, so the "setters" return a new object.
016 * <p>
017 * Here is an example of creating a custom {@code ConfigResolveOptions}:
018 * 
019 * <pre>
020 *     ConfigResolveOptions options = ConfigResolveOptions.defaults()
021 *         .setUseSystemEnvironment(false)
022 * </pre>
023 * <p>
024 * In addition to {@link ConfigResolveOptions#defaults}, there's a prebuilt
025 * {@link ConfigResolveOptions#noSystem} which avoids looking at any system
026 * environment variables or other external system information. (Right now,
027 * environment variables are the only example.)
028 */
029public final class ConfigResolveOptions {
030    private final boolean useSystemEnvironment;
031    private final boolean allowUnresolved;
032    private final ConfigResolver resolver;
033
034    private ConfigResolveOptions(boolean useSystemEnvironment, boolean allowUnresolved,
035                                 ConfigResolver resolver) {
036        this.useSystemEnvironment = useSystemEnvironment;
037        this.allowUnresolved = allowUnresolved;
038        this.resolver = resolver;
039    }
040
041    /**
042     * Returns the default resolve options. By default the system environment
043     * will be used and unresolved substitutions are not allowed.
044     * 
045     * @return the default resolve options
046     */
047    public static ConfigResolveOptions defaults() {
048        return new ConfigResolveOptions(true, false, NULL_RESOLVER);
049    }
050
051    /**
052     * Returns resolve options that disable any reference to "system" data
053     * (currently, this means environment variables).
054     *
055     * @return the resolve options with env variables disabled
056     */
057    public static ConfigResolveOptions noSystem() {
058        return defaults().setUseSystemEnvironment(false);
059    }
060
061    /**
062     * Returns options with use of environment variables set to the given value.
063     *
064     * @param value
065     *            true to resolve substitutions falling back to environment
066     *            variables.
067     * @return options with requested setting for use of environment variables
068     */
069    public ConfigResolveOptions setUseSystemEnvironment(boolean value) {
070        return new ConfigResolveOptions(value, allowUnresolved, resolver);
071    }
072
073    /**
074     * Returns whether the options enable use of system environment variables.
075     * This method is mostly used by the config lib internally, not by
076     * applications.
077     *
078     * @return true if environment variables should be used
079     */
080    public boolean getUseSystemEnvironment() {
081        return useSystemEnvironment;
082    }
083
084    /**
085     * Returns options with "allow unresolved" set to the given value. By
086     * default, unresolved substitutions are an error. If unresolved
087     * substitutions are allowed, then a future attempt to use the unresolved
088     * value may fail, but {@link Config#resolve(ConfigResolveOptions)} itself
089     * will not throw.
090     * 
091     * @param value
092     *            true to silently ignore unresolved substitutions.
093     * @return options with requested setting for whether to allow substitutions
094     * @since 1.2.0
095     */
096    public ConfigResolveOptions setAllowUnresolved(boolean value) {
097        return new ConfigResolveOptions(useSystemEnvironment, value, resolver);
098    }
099
100    /**
101     * Returns options where the given resolver used as a fallback if a
102     * reference cannot be otherwise resolved. This resolver will only be called
103     * after resolution has failed to substitute with a value from within the
104     * config itself and with any other resolvers that have been appended before
105     * this one. Multiple resolvers can be added using,
106     *
107     *  <pre>
108     *     ConfigResolveOptions options = ConfigResolveOptions.defaults()
109     *         .appendResolver(primary)
110     *         .appendResolver(secondary)
111     *         .appendResolver(tertiary);
112     * </pre>
113     *
114     * With this config unresolved references will first be resolved with the
115     * primary resolver, if that fails then the secondary, and finally if that
116     * also fails the tertiary.
117     *
118     * If all fallbacks fail to return a substitution "allow unresolved"
119     * determines whether resolution fails or continues.
120     *`
121     * @param value the resolver to fall back to
122     * @return options that use the given resolver as a fallback
123     * @since 1.3.2
124     */
125    public ConfigResolveOptions appendResolver(ConfigResolver value) {
126        if (value == null) {
127            throw new ConfigException.BugOrBroken("null resolver passed to appendResolver");
128        } else if (value == this.resolver) {
129            return this;
130        } else {
131            return new ConfigResolveOptions(useSystemEnvironment, allowUnresolved,
132                    this.resolver.withFallback(value));
133        }
134    }
135
136    /**
137     * Returns the resolver to use as a fallback if a substitution cannot be
138     * otherwise resolved. Never returns null. This method is mostly used by the
139     * config lib internally, not by applications.
140     *
141     * @return the non-null fallback resolver
142     * @since 1.3.2
143     */
144    public ConfigResolver getResolver() {
145        return this.resolver;
146    }
147
148    /**
149     * Returns whether the options allow unresolved substitutions. This method
150     * is mostly used by the config lib internally, not by applications.
151     * 
152     * @return true if unresolved substitutions are allowed
153     * @since 1.2.0
154     */
155    public boolean getAllowUnresolved() {
156        return allowUnresolved;
157    }
158
159    /**
160     * Singleton resolver that never resolves paths.
161     */
162    private static final ConfigResolver NULL_RESOLVER = new ConfigResolver() {
163
164        @Override
165        public ConfigValue lookup(String path) {
166            return null;
167        }
168
169        @Override
170        public ConfigResolver withFallback(ConfigResolver fallback) {
171            return fallback;
172        }
173
174    };
175
176}