Skip to content

Commit

Permalink
Add feature to write comments with snakeyaml; Resolves #10
Browse files Browse the repository at this point in the history
Introduce 3 modes of comment support for yaml. The first two modes
  correspond to the existing options. The newly added mode allows
  writing comments using new features of the SnakeYaml 1.28 API.
  It is recommended that users prefer the SnakeYaml 1.28 comment
  support, as it is the most robust and simultaneously featureful,
  though this may require upgrading the version of SnakeYaml used.
  • Loading branch information
A248 committed Mar 9, 2021
1 parent 59fd6af commit 0fc4b74
Show file tree
Hide file tree
Showing 13 changed files with 801 additions and 231 deletions.
2 changes: 1 addition & 1 deletion snakeyaml/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.27</version>
<version>1.28</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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.snakeyaml;

import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.YAMLException;

import java.io.IOException;
import java.io.Writer;
import java.util.List;
import java.util.Map;

final class BasicWriter implements YamlWriter {

private final Writer writer;
private final Yaml yaml;
private final CommentedWriter commentWriter;

BasicWriter(Writer writer, Yaml yaml, CommentedWriter commentWriter) {
this.writer = writer;
this.yaml = yaml;
this.commentWriter = commentWriter;
}

static final class Factory implements YamlWriter.Factory {

static final Factory INSTANCE = new Factory();

private Factory() {}

@Override
public YamlWriter newWriter(SnakeYamlOptions yamlOptions, Writer writer) {
return new BasicWriter(
writer,
yamlOptions.yamlSupplier().get(),
new CommentedWriter(writer, CommentMode.DEFAULT_COMMENT_FORMAT));
}

@Override
public boolean supportsComments() {
return false;
}

}

@Override
public void writeData(Map<String, Object> configMap, List<String> headerComments) throws IOException {
commentWriter.writeComments(headerComments);
try {
yaml.dump(configMap, writer);
} catch (YAMLException ex) {
throw yamlToIoException(ex);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* 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.snakeyaml;

import java.util.IllegalFormatException;
import java.util.Objects;

/**
* Determines the way in which comments will be written
*
*/
public final class CommentMode {

private final YamlWriter.Factory writerFactory;

static final String DEFAULT_COMMENT_FORMAT = " # %s";

CommentMode(YamlWriter.Factory writerFactory) {
this.writerFactory = writerFactory;
}

YamlWriter.Factory writerFactory() {
return writerFactory;
}

/**
* Whether this comment mode <i>fully</i> comments, including the comment header
* as well as comments on individual entries
*
* @return whether this mode fully supports comments
*/
public boolean supportsComments() {
return writerFactory().supportsComments();
}

/**
* Whether this comment mode is the same as another
*
* @param o the object to determine equality with
* @return true if equal, false otherwise
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CommentMode that = (CommentMode) o;
return writerFactory.equals(that.writerFactory);
}

@Override
public int hashCode() {
return writerFactory.hashCode();
}

@Override
public String toString() {
return "CommentMode{" +
"writerFactory=" + writerFactory +
'}';
}

/**
* A mode which will write the comment header on the top level configuration,
* but which is unable to write any further comments. <br>
* <br>
* This is the default mode, because it is most compatible with old versions
* of snakeyaml, and is highly stable.
*
* @return a comment mode which writes the top comment header only
*/
public static CommentMode headerOnly() {
return new CommentMode(BasicWriter.Factory.INSTANCE);
}

/**
* A mode which will use an alternative yaml writer (other than snakeyaml)
* to write both the comments of the configuration as well as the entries
* themselves. <br>
* <br>
* This implies full comment support, but note that the alternative yaml writer,
* while tested and workable, does not have the same level of stability as
* snakeyaml itself.
* <br>
* <b>The Comment Format</b> <br>
* The comment format is used for writing comments. It must be a {@code String.format}
* compatible format string. <br>
* <br>
* By default, comments are formatted as in the example:
* ' # Comment here'. This corresponds to the comment format {@code " # %s"} <br>
* <br>
* <b>Note well:</b> it is caller's responsibility to ensure the comment format results
* in valid YAML.
*
* @param commentFormat the comment format with which to write comments
* @return a comment mode using the alternative yaml writer
* @throws IllegalFormatException if the comment format is an illegal format string
*/
public static CommentMode alternativeWriter(String commentFormat) {
Objects.requireNonNull(commentFormat, "commentFormat");
String.format(commentFormat, "dummy comment");
return new CommentMode(new CommentedWriter.Factory(commentFormat));
}

/**
* A mode which will use an alternative yaml writer (other than snakeyaml)
* to write both the comments of the configuration as well as the entries
* themselves. <br>
* <br>
* This implies full comment support, but note that the alternative yaml writer,
* while tested and workable, does not have the same level of stability as
* snakeyaml itself. <br>
* <br>
* Uses the default comment format, i.e. ' # Comment here'
*
* @return a comment mode using the alternative yaml writer
*/
public static CommentMode alternativeWriter() {
return alternativeWriter(DEFAULT_COMMENT_FORMAT);
}

/**
* A mode which has full support for comments, taking advantage of functionality
* introduced in snakeyaml 1.28. If the version of snakeyaml in use is less
* than version 1.28, this option will fail at runtime (an undefined exception
* will be thrown at a later time)
*
* @return a mode which fully supports comments using snakeyaml 1.28 or a later version
*/
public static CommentMode fullComments() {
return new CommentMode(FullWriter.Factory.INSTANCE);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

import space.arim.dazzleconf.factory.CommentedWrapper;

class CommentedWriter {
final class CommentedWriter implements YamlWriter {

private final Writer writer;
private final String commentFormat;
Expand All @@ -46,6 +46,44 @@ class CommentedWriter {
this.commentFormat = commentFormat;
}

static final class Factory implements YamlWriter.Factory {

private final String commentFormat;

Factory(String commentFormat) {
this.commentFormat = commentFormat;
}

@Override
public CommentedWriter newWriter(SnakeYamlOptions yamlOptions, Writer writer) {
return new CommentedWriter(writer, commentFormat);
}

@Override
public boolean supportsComments() {
return true;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Factory factory = (Factory) o;
return commentFormat.equals(factory.commentFormat);
}

@Override
public int hashCode() {
return commentFormat.hashCode();
}
}

@Override
public void writeData(Map<String, Object> configMap, List<String> headerComments) throws IOException {
writeComments(headerComments);
writeMap(configMap);
}

/**
* Writes a config map
*
Expand Down
Loading

0 comments on commit 0fc4b74

Please sign in to comment.