All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.config.hocon.internal.HoconConfigParser Maven / Gradle / Ivy

/*
 * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.helidon.config.hocon.internal;

import java.util.Objects;
import java.util.Set;

import javax.annotation.Priority;

import io.helidon.common.CollectionsHelper;
import io.helidon.config.ConfigException;
import io.helidon.config.ConfigHelper;
import io.helidon.config.spi.ConfigNode.ListNode;
import io.helidon.config.spi.ConfigNode.ObjectNode;
import io.helidon.config.spi.ConfigParser;
import io.helidon.config.spi.ConfigParserException;

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigList;
import com.typesafe.config.ConfigObject;
import com.typesafe.config.ConfigResolveOptions;
import com.typesafe.config.ConfigValue;

/**
 * Typesafe (Lightbend) Config (HOCON) {@link ConfigParser} implementation that supports following media types:
 * {@value #MEDIA_TYPE_APPLICATION_HOCON} and
 * {@value #MEDIA_TYPE_APPLICATION_JSON}.
 * 

* The parser implementation supports {@link java.util.ServiceLoader}, i.e. {@link io.helidon.config.Config.Builder} * can automatically load and register {@code HoconConfigParser} instance, * if not {@link io.helidon.config.Config.Builder#disableParserServices() disabled}. * And of course it can be {@link io.helidon.config.Config.Builder#addParser(ConfigParser) registered programmatically}. *

* Priority of the {@code HoconConfigParser} to be used by {@link io.helidon.config.Config.Builder}, * if loaded automatically as a {@link java.util.ServiceLoader service}, is {@value PRIORITY}. * * @see io.helidon.config.Config.Builder#addParser(ConfigParser) * @see io.helidon.config.Config.Builder#disableParserServices() */ @Priority(HoconConfigParser.PRIORITY) public class HoconConfigParser implements ConfigParser { /** * A String constant representing {@value} media type. */ public static final String MEDIA_TYPE_APPLICATION_HOCON = "application/hocon"; /** * A String constant representing {@value} media type. */ public static final String MEDIA_TYPE_APPLICATION_JSON = "application/json"; /** * Priority of the parser used if registered by {@link io.helidon.config.Config.Builder} automatically. */ public static final int PRIORITY = ConfigParser.PRIORITY + 100; private static final Set SUPPORTED_MEDIA_TYPES = CollectionsHelper.setOf(MEDIA_TYPE_APPLICATION_HOCON, MEDIA_TYPE_APPLICATION_JSON); private final boolean resolvingEnabled; private final ConfigResolveOptions resolveOptions; /** * Initializes parser. * * @param resolvingEnabled resolving substitutions support enabled * @param resolveOptions resolving options */ public HoconConfigParser(boolean resolvingEnabled, ConfigResolveOptions resolveOptions) { if (resolvingEnabled) { Objects.requireNonNull(resolveOptions, "resolveOptions parameter is mandatory"); } this.resolvingEnabled = resolvingEnabled; this.resolveOptions = resolveOptions; } /** * Initializes HOCON Config Parser with {@link Config#resolve() HOCON resolving substitutions support} enabled. * {@link ConfigResolveOptions#defaults()} is used to resolve loaded configuration. */ public HoconConfigParser() { this(true, ConfigResolveOptions.defaults()); } @Override public Set supportedMediaTypes() { return SUPPORTED_MEDIA_TYPES; } @Override public ObjectNode parse(Content content) { Config typesafeConfig; try (AutoCloseable readable = content.asReadable()) { typesafeConfig = ConfigFactory.parseReader(ConfigHelper.createReader((Readable) readable)); if (resolvingEnabled) { typesafeConfig = typesafeConfig.resolve(resolveOptions); } return fromConfig(typesafeConfig.root()); } catch (ConfigException e) { throw e; } catch (Exception e) { throw new ConfigParserException("Cannot read from source: " + e.getLocalizedMessage(), e); } } private static ObjectNode fromConfig(ConfigObject config) { ObjectNode.Builder builder = ObjectNode.builder(); config.entrySet() .forEach(entry -> { String key = io.helidon.config.Config.Key.escapeName(entry.getKey()); ConfigValue value = entry.getValue(); if (value instanceof ConfigList) { builder.addList(key, fromList((ConfigList) value)); } else if (value instanceof ConfigObject) { builder.addObject(key, fromConfig((ConfigObject) value)); } else { builder.addValue(key, value.unwrapped().toString()); } }); return builder.build(); } private static ListNode fromList(ConfigList list) { ListNode.Builder builder = ListNode.builder(); list.forEach(value -> { if (value instanceof ConfigList) { builder.addList(fromList((ConfigList) value)); } else if (value instanceof ConfigObject) { builder.addObject(fromConfig((ConfigObject) value)); } else { builder.addValue(value.unwrapped().toString()); } }); return builder.build(); } }