    001/**
    002 *   Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com>
    003 */
    004package com.typesafe.config;
    005
    006
    007/**
    008 * A set of options related to parsing.
    009 *
    010 * <p>
    011 * This object is immutable, so the "setters" return a new object.
    012 *
    013 * <p>
    014 * Here is an example of creating a custom {@code ConfigParseOptions}:
    015 *
    016 * <pre>
    017 *     ConfigParseOptions options = ConfigParseOptions.defaults()
    018 *         .setSyntax(ConfigSyntax.JSON)
    019 *         .setAllowMissing(false)
    020 * </pre>
    021 *
    022 */
    023public final class ConfigParseOptions {
    024    final ConfigSyntax syntax;
    025    final String originDescription;
    026    final boolean allowMissing;
    027    final ConfigIncluder includer;
    028    final ClassLoader classLoader;
    029
    030    private ConfigParseOptions(ConfigSyntax syntax, String originDescription, boolean allowMissing,
    031            ConfigIncluder includer, ClassLoader classLoader) {
    032        this.syntax = syntax;
    033        this.originDescription = originDescription;
    034        this.allowMissing = allowMissing;
    035        this.includer = includer;
    036        this.classLoader = classLoader;
    037    }
    038
    039    /**
    040     * Gets an instance of <code>ConfigParseOptions</code> with all fields
    041     * set to the default values. Start with this instance and make any
    042     * changes you need.
    043     * @return the default parse options
    044     */
    045    public static ConfigParseOptions defaults() {
    046        return new ConfigParseOptions(null, null, true, null, null);
    047    }
    048
    049    /**
    050     * Set the file format. If set to null, try to guess from any available
    051     * filename extension; if guessing fails, assume {@link ConfigSyntax#CONF}.
    052     *
    053     * @param syntax
    054     *            a syntax or {@code null} for best guess
    055     * @return options with the syntax set
    056     */
    057    public ConfigParseOptions setSyntax(ConfigSyntax syntax) {
    058        if (this.syntax == syntax)
    059            return this;
    060        else
    061            return new ConfigParseOptions(syntax, this.originDescription, this.allowMissing,
    062                    this.includer, this.classLoader);
    063    }
    064
    065    /**
    066     * Gets the current syntax option, which may be null for "any".
    067     * @return the current syntax or null
    068     */
    069    public ConfigSyntax getSyntax() {
    070        return syntax;
    071    }
    072
    073    /**
    074     * Set a description for the thing being parsed. In most cases this will be
    075     * set up for you to something like the filename, but if you provide just an
    076     * input stream you might want to improve on it. Set to null to allow the
    077     * library to come up with something automatically. This description is the
    078     * basis for the {@link ConfigOrigin} of the parsed values.
    079     *
    080     * @param originDescription description to put in the {@link ConfigOrigin}
    081     * @return options with the origin description set
    082     */
    083    public ConfigParseOptions setOriginDescription(String originDescription) {
    084        // findbugs complains about == here but is wrong, do not "fix"
    085        if (this.originDescription == originDescription)
    086            return this;
    087        else if (this.originDescription != null && originDescription != null
    088                && this.originDescription.equals(originDescription))
    089            return this;
    090        else
    091            return new ConfigParseOptions(this.syntax, originDescription, this.allowMissing,
    092                    this.includer, this.classLoader);
    093    }
    094
    095    /**
    096     * Gets the current origin description, which may be null for "automatic".
    097     * @return the current origin description or null
    098     */
    099    public String getOriginDescription() {
    100        return originDescription;
    101    }
    102
    103    /** this is package-private, not public API */
    104    ConfigParseOptions withFallbackOriginDescription(String originDescription) {
    105        if (this.originDescription == null)
    106            return setOriginDescription(originDescription);
    107        else
    108            return this;
    109    }
    110
    111    /**
    112     * Set to false to throw an exception if the item being parsed (for example
    113     * a file) is missing. Set to true to just return an empty document in that
    114     * case. Note that this setting applies on only to fetching the root document,
    115     * it has no effect on any nested includes.
    116     *
    117     * @param allowMissing true to silently ignore missing item
    118     * @return options with the "allow missing" flag set
    119     */
    120    public ConfigParseOptions setAllowMissing(boolean allowMissing) {
    121        if (this.allowMissing == allowMissing)
    122            return this;
    123        else
    124            return new ConfigParseOptions(this.syntax, this.originDescription, allowMissing,
    125                    this.includer, this.classLoader);
    126    }
    127
    128    /**
    129     * Gets the current "allow missing" flag.
    130     * @return whether we allow missing files
    131     */
    132    public boolean getAllowMissing() {
    133        return allowMissing;
    134    }
    135
    136    /**
    137     * Set a {@link ConfigIncluder} which customizes how includes are handled.
    138     * null means to use the default includer.
    139     *
    140     * @param includer the includer to use or null for default
    141     * @return new version of the parse options with different includer
    142     */
    143    public ConfigParseOptions setIncluder(ConfigIncluder includer) {
    144        if (this.includer == includer)
    145            return this;
    146        else
    147            return new ConfigParseOptions(this.syntax, this.originDescription, this.allowMissing,
    148                    includer, this.classLoader);
    149    }
    150
    151    /**
    152     * Prepends a {@link ConfigIncluder} which customizes how
    153     * includes are handled.  To prepend your includer, the
    154     * library calls {@link ConfigIncluder#withFallback} on your
    155     * includer to append the existing includer to it.
    156     *
    157     * @param includer the includer to prepend (may not be null)
    158     * @return new version of the parse options with different includer
    159     */
    160    public ConfigParseOptions prependIncluder(ConfigIncluder includer) {
    161        if (includer == null)
    162            throw new NullPointerException("null includer passed to prependIncluder");
    163        if (this.includer == includer)
    164            return this;
    165        else if (this.includer != null)
    166            return setIncluder(includer.withFallback(this.includer));
    167        else
    168            return setIncluder(includer);
    169    }
    170
    171    /**
    172     * Appends a {@link ConfigIncluder} which customizes how
    173     * includes are handled.  To append, the library calls {@link
    174     * ConfigIncluder#withFallback} on the existing includer.
    175     *
    176     * @param includer the includer to append (may not be null)
    177     * @return new version of the parse options with different includer
    178     */
    179    public ConfigParseOptions appendIncluder(ConfigIncluder includer) {
    180        if (includer == null)
    181            throw new NullPointerException("null includer passed to appendIncluder");
    182        if (this.includer == includer)
    183            return this;
    184        else if (this.includer != null)
    185            return setIncluder(this.includer.withFallback(includer));
    186        else
    187            return setIncluder(includer);
    188    }
    189
    190    /**
    191     * Gets the current includer (will be null for the default includer).
    192     * @return current includer or null
    193     */
    194    public ConfigIncluder getIncluder() {
    195        return includer;
    196    }
    197
    198    /**
    199     * Set the class loader. If set to null,
    200     * <code>Thread.currentThread().getContextClassLoader()</code> will be used.
    201     *
    202     * @param loader
    203     *            a class loader or {@code null} to use thread context class
    204     *            loader
    205     * @return options with the class loader set
    206     */
    207    public ConfigParseOptions setClassLoader(ClassLoader loader) {
    208        if (this.classLoader == loader)
    209            return this;
    210        else
    211            return new ConfigParseOptions(this.syntax, this.originDescription, this.allowMissing,
    212                    this.includer, loader);
    213    }
    214
    215    /**
    216     * Get the class loader; never returns {@code null}, if the class loader was
    217     * unset, returns
    218     * <code>Thread.currentThread().getContextClassLoader()</code>.
    219     *
    220     * @return class loader to use
    221     */
    222    public ClassLoader getClassLoader() {
    223        if (this.classLoader == null)
    224            return Thread.currentThread().getContextClassLoader();
    225        else
    226            return this.classLoader;
    227    }
    228}