Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hocon support #6

Merged
merged 17 commits into from
Mar 8, 2021
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions hocon/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<artifactId>dazzleconf-parent</artifactId>
<groupId>space.arim.dazzleconf</groupId>
<version>1.2.0-SNAPSHOT</version>
</parent>

<artifactId>dazzleconf-ext-hocon</artifactId>

<dependencies>
<dependency>
<groupId>space.arim.dazzleconf</groupId>
<artifactId>dazzleconf-core</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
</dependencies>

</project>
6 changes: 6 additions & 0 deletions hocon/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module space.arim.dazzleconf.ext.hocon {
exports space.arim.dazzleconf.ext.hocon;

requires transitive typesafe.config;
requires transitive space.arim.dazzleconf;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package space.arim.dazzleconf.ext.hocon;

import space.arim.dazzleconf.ConfigurationFactory;
import space.arim.dazzleconf.ConfigurationOptions;
import space.arim.dazzleconf.error.IllDefinedConfigException;

/**
* Allows creating a {@link ConfigurationFactory} implementation using Hocon.
*
*/
public final class HoconConfigurationFactory {

private HoconConfigurationFactory() {}

/**
* Creates from a config class, config options, and hocon options
*
* @param <C> the configuration type
* @param configClass the config class
* @param options configuration options
* @param hoconOptions parsing and reading options, can not be null
* @return the configuration factory
* @throws NullPointerException if any parameter is null
* @throws IllegalArgumentException if {@code configClass} is not an interface
* @throws IllDefinedConfigException if the configuration entries defined in the config class are
* invalid
*/
public static <C> ConfigurationFactory<C> create(Class<C> configClass, ConfigurationOptions options,
HoconOptions hoconOptions) {
return new HoconConfigurationFactoryImpl<>(configClass, options, hoconOptions);
}

/**
* Creates from a config class and config options. <br>
* <br>
* Uses the default hocon options
*
* @param <C> the configuration type
* @param configClass the config class
* @param options configuration options
* @return the configuration factory
* @throws NullPointerException if either parameter is null
* @throws IllegalArgumentException if {@code configClass} is not an interface
* @throws IllDefinedConfigException if the configuration entries defined in the config class are
* invalid
*/
public static <C> ConfigurationFactory<C> create(Class<C> configClass, ConfigurationOptions options) {
return create(configClass, options, new HoconOptions.Builder().build());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package space.arim.dazzleconf.ext.hocon;

import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigObject;
import com.typesafe.config.ConfigValue;
import com.typesafe.config.ConfigValueFactory;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import space.arim.dazzleconf.ConfigurationOptions;
import space.arim.dazzleconf.error.BadValueException;
import space.arim.dazzleconf.error.ConfigFormatSyntaxException;
import space.arim.dazzleconf.error.InvalidConfigException;
import space.arim.dazzleconf.error.MissingValueException;
import space.arim.dazzleconf.factory.CommentedWrapper;
import space.arim.dazzleconf.factory.HumanReadableConfigurationFactory;

final class HoconConfigurationFactoryImpl<C> extends HumanReadableConfigurationFactory<C> {

private final HoconOptions hoconOptions;

HoconConfigurationFactoryImpl(Class<C> configClass, ConfigurationOptions options,
HoconOptions hoconOptions) {
super(configClass, options);
this.hoconOptions = Objects.requireNonNull(hoconOptions);
}

@Override
public Charset charset() {
return hoconOptions.charset();
}

@Override
public Map<String, Object> loadMap(Reader reader) throws IOException, InvalidConfigException {
try {
return ConfigFactory.parseReader(reader, hoconOptions.configParseOptions()).root().unwrapped();

} catch (ConfigException.BadValue | ConfigException.Null e) {
throw new BadValueException.Builder().cause(e).build();

} catch (ConfigException.IO e) {
throw new IOException(e);

} catch (ConfigException.Missing e) {
MissingValueException mve = MissingValueException.forKey("unknown");
mve.initCause(e);
throw mve;

} catch (ConfigException.Parse e) {
throw new ConfigFormatSyntaxException(e);

} catch (ConfigException e) {
throw new IOException("Unexpected ConfigException subclass", e);
}
}

@Override
public void writeMap(Map<String, Object> config, Writer writer) throws IOException {
ConfigObject hoconConfig = convertMapToHocon(config);
List<String> commentHeader = getHeader();
if (!commentHeader.isEmpty()) {
hoconConfig = hoconConfig.withOrigin(hoconConfig.origin().withComments(commentHeader));
}

writer.write(hoconConfig.render(hoconOptions.configRenderOptions()));
}

private ConfigObject convertMapToHocon(Map<String, Object> config) {
Map<String, Object> hoconConfigMap = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : config.entrySet()) {
hoconConfigMap.put(entry.getKey(), convertValueToHocon(entry.getValue()));
}

return ConfigValueFactory.fromMap(hoconConfigMap);
}

private ConfigValue convertValueToHocon(Object value) {
if (value instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) value;

return convertMapToHocon(map);
}
if (value instanceof CommentedWrapper) {
CommentedWrapper commentedWrapper = (CommentedWrapper) value;
ConfigValue hoconValue = convertValueToHocon(commentedWrapper.getValue());

return hoconValue.withOrigin(hoconValue.origin().withComments(commentedWrapper.getComments()));
}

return ConfigValueFactory.fromAnyRef(value);
}

@Override
public boolean supportsCommentsThroughWrapper() {
return true;
}
}
103 changes: 103 additions & 0 deletions hocon/src/main/java/space/arim/dazzleconf/ext/hocon/HoconOptions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package space.arim.dazzleconf.ext.hocon;

import com.typesafe.config.ConfigParseOptions;
import com.typesafe.config.ConfigRenderOptions;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

public final class HoconOptions {
private final ConfigParseOptions configParseOptions;
private final ConfigRenderOptions configRenderOptions;
private final Charset charset;

HoconOptions(Builder builder) {
this.configParseOptions = builder.configParseOptions;
this.configRenderOptions = builder.configRenderOptions;
this.charset = builder.charset;
}

/**
* Gets used parse options
*
* @return used parse options
*/
public ConfigParseOptions configParseOptions() {
return configParseOptions;
}

/**
* Gets used render options.
*
* @return used render options
*/
public ConfigRenderOptions configRenderOptions() {
return configRenderOptions;
}

/**
* Gets used charset
*
* @return used charset
*/
public Charset charset() {
return charset;
}

/**
* Builder of {@code HoconOptions}
*
* @author CDFN
*/
public final static class Builder {
ConfigParseOptions configParseOptions = ConfigParseOptions.defaults();
ConfigRenderOptions configRenderOptions = ConfigRenderOptions.defaults()
.setOriginComments(false)
.setJson(false);

Charset charset = StandardCharsets.UTF_8;

/**
* Sets parse options. Reading config uses them.
*
* @param options the options
* @return this builder
*/
public Builder configParseOptions(ConfigParseOptions options) {
this.configParseOptions = Objects.requireNonNull(options);
return this;
}

/**
* Sets rendering options. Writing config uses them.
*
* @param options the options
* @return this builder
*/
public Builder configRenderOptions(ConfigRenderOptions options) {
this.configRenderOptions = Objects.requireNonNull(options);
return this;
}

/**
* Sets the charset used by the factory. Default is UTF 8
*
* @param charset the charset
* @return this builder
*/
public Builder charset(Charset charset) {
this.charset = Objects.requireNonNull(charset);
return this;
}

/**
* Builds the options. May be used repeatedly without side effects
*
* @return the built options
*/
public HoconOptions build() {
return new HoconOptions(this);
}
}
}
49 changes: 49 additions & 0 deletions hocon/src/test/java/space/arim/dazzleconf/ext/hocon/Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* DazzleConf
* Copyright © 2021 Anand Beh
*
* DazzleConf is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DazzleConf is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with DazzleConf. If not, see <https://www.gnu.org/licenses/>
* and navigate to version 3 of the GNU Lesser General Public License.
*/

package space.arim.dazzleconf.ext.hocon;

import space.arim.dazzleconf.annote.ConfComments;
import space.arim.dazzleconf.annote.ConfDefault.DefaultBoolean;
import space.arim.dazzleconf.annote.ConfDefault.DefaultString;
import space.arim.dazzleconf.annote.ConfHeader;
import space.arim.dazzleconf.annote.ConfKey;
import space.arim.dazzleconf.annote.SubSection;

public interface Config {

@ConfKey("some-option")
@DefaultString("option-value")
@ConfComments({"Comment using Hocon", "Another line of comments"})
String someOption();

@ConfKey("section-one")
@SubSection
ConfigSection sectionOne();

@ConfHeader("Section header")
interface ConfigSection {

@ConfKey("some-flag")
@DefaultBoolean(false)
boolean someFlag();

}

}
Loading