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}