    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.
    115     *
    116     * @param allowMissing true to silently ignore missing item
    117     * @return options with the "allow missing" flag set
    118     */
    119    public ConfigParseOptions setAllowMissing(boolean allowMissing) {
    120        if (this.allowMissing == allowMissing)
    121            return this;
    122        else
    123            return new ConfigParseOptions(this.syntax, this.originDescription, allowMissing,
    124                    this.includer, this.classLoader);
    125    }
    126
    127    /**
    128     * Gets the current "allow missing" flag.
    129     * @return whether we allow missing files
    130     */
    131    public boolean getAllowMissing() {
    132        return allowMissing;
    133    }
    134
    135    /**
    136     * Set a {@link ConfigIncluder} which customizes how includes are handled.
    137     * null means to use the default includer.
    138     *
    139     * @param includer the includer to use or null for default
    140     * @return new version of the parse options with different includer
    141     */
    142    public ConfigParseOptions setIncluder(ConfigIncluder includer) {
    143        if (this.includer == includer)
    144            return this;
    145        else
    146            return new ConfigParseOptions(this.syntax, this.originDescription, this.allowMissing,
    147                    includer, this.classLoader);
    148    }
    149
    150    /**
    151     * Prepends a {@link ConfigIncluder} which customizes how
    152     * includes are handled.  To prepend your includer, the
    153     * library calls {@link ConfigIncluder#withFallback} on your
    154     * includer to append the existing includer to it.
    155     *
    156     * @param includer the includer to prepend (may not be null)
    157     * @return new version of the parse options with different includer
    158     */
    159    public ConfigParseOptions prependIncluder(ConfigIncluder includer) {
    160        if (includer == null)
    161            throw new NullPointerException("null includer passed to prependIncluder");
    162        if (this.includer == includer)
    163            return this;
    164        else if (this.includer != null)
    165            return setIncluder(includer.withFallback(this.includer));
    166        else
    167            return setIncluder(includer);
    168    }
    169
    170    /**
    171     * Appends a {@link ConfigIncluder} which customizes how
    172     * includes are handled.  To append, the library calls {@link
    173     * ConfigIncluder#withFallback} on the existing includer.
    174     *
    175     * @param includer the includer to append (may not be null)
    176     * @return new version of the parse options with different includer
    177     */
    178    public ConfigParseOptions appendIncluder(ConfigIncluder includer) {
    179        if (includer == null)
    180            throw new NullPointerException("null includer passed to appendIncluder");
    181        if (this.includer == includer)
    182            return this;
    183        else if (this.includer != null)
    184            return setIncluder(this.includer.withFallback(includer));
    185        else
    186            return setIncluder(includer);
    187    }
    188
    189    /**
    190     * Gets the current includer (will be null for the default includer).
    191     * @return current includer or null
    192     */
    193    public ConfigIncluder getIncluder() {
    194        return includer;
    195    }
    196
    197    /**
    198     * Set the class loader. If set to null,
    199     * <code>Thread.currentThread().getContextClassLoader()</code> will be used.
    200     *
    201     * @param loader
    202     *            a class loader or {@code null} to use thread context class
    203     *            loader
    204     * @return options with the class loader set
    205     */
    206    public ConfigParseOptions setClassLoader(ClassLoader loader) {
    207        if (this.classLoader == loader)
    208            return this;
    209        else
    210            return new ConfigParseOptions(this.syntax, this.originDescription, this.allowMissing,
    211                    this.includer, loader);
    212    }
    213
    214    /**
    215     * Get the class loader; never returns {@code null}, if the class loader was
    216     * unset, returns
    217     * <code>Thread.currentThread().getContextClassLoader()</code>.
    218     *
    219     * @return class loader to use
    220     */
    221    public ClassLoader getClassLoader() {
    222        if (this.classLoader == null)
    223            return Thread.currentThread().getContextClassLoader();
    224        else
    225            return this.classLoader;
    226    }
    227}