    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.configuration2;
    019
    020import org.apache.commons.configuration2.ex.ConfigurationException;
    021import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
    022import org.apache.commons.configuration2.io.InputStreamSupport;
    023import org.apache.commons.configuration2.tree.ImmutableNode;
    024import org.yaml.snakeyaml.DumperOptions;
    025import org.yaml.snakeyaml.LoaderOptions;
    026import org.yaml.snakeyaml.Yaml;
    027import org.yaml.snakeyaml.constructor.Constructor;
    028import org.yaml.snakeyaml.representer.Representer;
    029
    030import java.io.IOException;
    031import java.io.InputStream;
    032import java.io.Reader;
    033import java.io.Writer;
    034import java.util.Map;
    035
    036/**
    037 * <p>
    038 * A specialized hierarchical configuration class that is able to parse YAML
    039 * documents.
    040 * </p>
    041 *
    042 * @since 2.2
    043 */
    044public class YAMLConfiguration extends AbstractYAMLBasedConfiguration
    045        implements FileBasedConfiguration, InputStreamSupport
    046{
    047    /**
    048     * Creates a new instance of {@code YAMLConfiguration}.
    049     */
    050    public YAMLConfiguration()
    051    {
    052        super();
    053    }
    054
    055    /**
    056     * Creates a new instance of {@code YAMLConfiguration} as a copy of the
    057     * specified configuration.
    058     *
    059     * @param c the configuration to be copied
    060     */
    061    public YAMLConfiguration(final HierarchicalConfiguration<ImmutableNode> c)
    062    {
    063        super(c);
    064    }
    065
    066    @Override
    067    public void read(final Reader in) throws ConfigurationException
    068    {
    069        try
    070        {
    071            final Yaml yaml = createYamlForReading(new LoaderOptions());
    072            final Map<String, Object> map = (Map) yaml.load(in);
    073            load(map);
    074        }
    075        catch (final Exception e)
    076        {
    077            rethrowException(e);
    078        }
    079    }
    080
    081    public void read(final Reader in, final LoaderOptions options)
    082            throws ConfigurationException
    083    {
    084        try
    085        {
    086            final Yaml yaml = createYamlForReading(options);
    087            final Map<String, Object> map = (Map) yaml.load(in);
    088            load(map);
    089        }
    090        catch (final Exception e)
    091        {
    092            rethrowException(e);
    093        }
    094    }
    095
    096    @Override
    097    public void write(final Writer out) throws ConfigurationException, IOException
    098    {
    099        final DumperOptions options = new DumperOptions();
    100        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
    101        dump(out, options);
    102    }
    103
    104    public void dump(final Writer out, final DumperOptions options)
    105            throws ConfigurationException, IOException
    106    {
    107        final Yaml yaml = new Yaml(options);
    108        yaml.dump(constructMap(getNodeModel().getNodeHandler().getRootNode()),
    109                out);
    110    }
    111
    112    /**
    113     * Loads the configuration from the given input stream.
    114     *
    115     * @param in the input stream
    116     * @throws ConfigurationException if an error occurs
    117     */
    118    @Override
    119    public void read(final InputStream in) throws ConfigurationException
    120    {
    121        try
    122        {
    123            final Yaml yaml = createYamlForReading(new LoaderOptions());
    124            final Map<String, Object> map = (Map) yaml.load(in);
    125            load(map);
    126        }
    127        catch (final Exception e)
    128        {
    129            rethrowException(e);
    130        }
    131    }
    132
    133    public void read(final InputStream in, final LoaderOptions options)
    134            throws ConfigurationException
    135    {
    136        try
    137        {
    138            final Yaml yaml = createYamlForReading(options);
    139            final Map<String, Object> map = (Map) yaml.load(in);
    140            load(map);
    141        }
    142        catch (final Exception e)
    143        {
    144            rethrowException(e);
    145        }
    146    }
    147
    148    /**
    149     * Creates a {@code Yaml} object for reading a Yaml file. The object is
    150     * configured with some default settings.
    151     *
    152     * @param options options for loading the file
    153     * @return the {@code Yaml} instance for loading a file
    154     */
    155    private static Yaml createYamlForReading(LoaderOptions options)
    156    {
    157        return new Yaml(createClassLoadingDisablingConstructor(), new Representer(), new DumperOptions(), options);
    158    }
    159
    160    /**
    161     * Returns a {@code Constructor} object for the YAML parser that prevents
    162     * all classes from being loaded. This effectively disables the dynamic
    163     * creation of Java objects that are declared in YAML files to be loaded.
    164     *
    165     * @return the {@code Constructor} preventing object creation
    166     */
    167    private static Constructor createClassLoadingDisablingConstructor()
    168    {
    169        return new Constructor()
    170        {
    171            @Override
    172            protected Class<?> getClassForName(String name)
    173            {
    174                throw new ConfigurationRuntimeException("Class instantiation is disabled.");
    175            }
    176        };
    177    }
    178}