Skip to content

Commit

Permalink
Enable user-provided YAML configuration objects
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Edgar <[email protected]>
  • Loading branch information
MikeEdgar committed Feb 27, 2024
1 parent 1fcf9d3 commit 4daadd1
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 55 deletions.
22 changes: 17 additions & 5 deletions src/main/java/io/xlate/yamljson/SnakeYamlEngineGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ class SnakeYamlEngineGenerator extends YamlGenerator<Event, ScalarStyle> impleme

static final Map<EventType, Event> EVENTS = new EnumMap<>(EventType.class);
static final Map<StyleType, ScalarStyle> STYLES = new EnumMap<>(StyleType.class);
static final Event DOCUMENT_START_DEFAULT = new DocumentStartEvent(false, Optional.empty(), Collections.emptyMap());
static final Event DOCUMENT_START_EXPLICIT = new DocumentStartEvent(true, Optional.empty(), Collections.emptyMap());
static final Event DOCUMENT_END_DEFAULT = new DocumentEndEvent(false);
static final Event DOCUMENT_END_EXPLICIT = new DocumentEndEvent(true);

static {
EVENTS.put(EventType.STREAM_START, new StreamStartEvent());
EVENTS.put(EventType.STREAM_END, new StreamEndEvent());
EVENTS.put(EventType.DOCUMENT_START_DEFAULT, new DocumentStartEvent(false, Optional.empty(), Collections.emptyMap()));
EVENTS.put(EventType.DOCUMENT_START_EXPLICIT, new DocumentStartEvent(true, Optional.empty(), Collections.emptyMap()));
EVENTS.put(EventType.DOCUMENT_END_DEFAULT, new DocumentEndEvent(false));
EVENTS.put(EventType.DOCUMENT_END_EXPLICIT, new DocumentEndEvent(true));
EVENTS.put(EventType.MAPPING_START, new MappingStartEvent(Optional.empty(), Optional.empty(), true, FlowStyle.AUTO));
EVENTS.put(EventType.MAPPING_END, new MappingEndEvent());
EVENTS.put(EventType.SEQUENCE_START, new SequenceStartEvent(Optional.empty(), Optional.empty(), true, FlowStyle.AUTO));
Expand All @@ -68,7 +68,7 @@ class SnakeYamlEngineGenerator extends YamlGenerator<Event, ScalarStyle> impleme
final Emitter emitter;

SnakeYamlEngineGenerator(DumpSettings settings, YamlWriterStream writer) {
super(EVENTS, STYLES, writer, settings.isExplicitStart(), settings.isExplicitEnd());
super(STYLES, writer);
this.settings = settings;
this.emitter = new Emitter(settings, writer);
}
Expand All @@ -77,6 +77,18 @@ class SnakeYamlEngineGenerator extends YamlGenerator<Event, ScalarStyle> impleme
this(settings, new YamlWriterStream(writer));
}

@Override
protected Event getEvent(EventType type) {
switch (type) {
case DOCUMENT_START:
return settings.isExplicitStart() ? DOCUMENT_START_EXPLICIT : DOCUMENT_START_DEFAULT;
case DOCUMENT_END:
return settings.isExplicitEnd() ? DOCUMENT_END_EXPLICIT : DOCUMENT_END_DEFAULT;
default:
return EVENTS.get(type);
}
}

@Override
protected void emitEvent(Event event) {
emitter.emit(event);
Expand Down
26 changes: 19 additions & 7 deletions src/main/java/io/xlate/yamljson/SnakeYamlGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.util.Map;
import java.util.stream.Stream;

import jakarta.json.stream.JsonGenerator;

import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
Expand All @@ -38,22 +40,20 @@
import org.yaml.snakeyaml.events.StreamEndEvent;
import org.yaml.snakeyaml.events.StreamStartEvent;

import jakarta.json.stream.JsonGenerator;

class SnakeYamlGenerator extends YamlGenerator<Event, ScalarStyle> implements JsonGenerator {

static final ImplicitTuple omitTags = new ImplicitTuple(true, true);

static final Map<EventType, Event> EVENTS = new EnumMap<>(EventType.class);
static final Map<StyleType, ScalarStyle> STYLES = new EnumMap<>(StyleType.class);
static final Event DOCUMENT_START_DEFAULT = new DocumentStartEvent(null, null, false, null, Collections.emptyMap());
static final Event DOCUMENT_START_EXPLICIT = new DocumentStartEvent(null, null, true, null, Collections.emptyMap());
static final Event DOCUMENT_END_DEFAULT = new DocumentEndEvent(null, null, false);
static final Event DOCUMENT_END_EXPLICIT = new DocumentEndEvent(null, null, true);

static {
EVENTS.put(EventType.STREAM_START, new StreamStartEvent(null, null));
EVENTS.put(EventType.STREAM_END, new StreamEndEvent(null, null));
EVENTS.put(EventType.DOCUMENT_START_DEFAULT, new DocumentStartEvent(null, null, false, null, Collections.emptyMap()));
EVENTS.put(EventType.DOCUMENT_START_EXPLICIT, new DocumentStartEvent(null, null, true, null, Collections.emptyMap()));
EVENTS.put(EventType.DOCUMENT_END_DEFAULT, new DocumentEndEvent(null, null, false));
EVENTS.put(EventType.DOCUMENT_END_EXPLICIT, new DocumentEndEvent(null, null, true));
EVENTS.put(EventType.MAPPING_START, new MappingStartEvent(null, null, true, null, null, FlowStyle.AUTO));
EVENTS.put(EventType.MAPPING_END, new MappingEndEvent(null, null));
EVENTS.put(EventType.SEQUENCE_START, new SequenceStartEvent(null, null, true, null, null, FlowStyle.AUTO));
Expand All @@ -66,11 +66,23 @@ class SnakeYamlGenerator extends YamlGenerator<Event, ScalarStyle> implements Js
final Emitter emitter;

SnakeYamlGenerator(DumperOptions settings, Writer writer) {
super(EVENTS, STYLES, writer, settings.isExplicitStart(), settings.isExplicitEnd());
super(STYLES, writer);
this.settings = settings;
this.emitter = new Emitter(writer, settings);
}

@Override
protected Event getEvent(EventType type) {
switch (type) {
case DOCUMENT_START:
return settings.isExplicitStart() ? DOCUMENT_START_EXPLICIT : DOCUMENT_START_DEFAULT;
case DOCUMENT_END:
return settings.isExplicitEnd() ? DOCUMENT_END_EXPLICIT : DOCUMENT_END_DEFAULT;
default:
return EVENTS.get(type);
}
}

@Override
protected void emitEvent(Event event) throws IOException {
emitter.emit(event);
Expand Down
53 changes: 47 additions & 6 deletions src/main/java/io/xlate/yamljson/Yaml.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ private Settings() {
*
* @see Versions#V1_1
* @see Versions#V1_2
*
*/
public static final String YAML_VERSION = "io.xlate.yamljson.YAML_VERSION";

Expand All @@ -128,26 +127,68 @@ private Settings() {
* @see org.snakeyaml.engine.v2.api.LoadSettingsBuilder#setUseMarks(boolean)
*
* @since 0.1.0
* @deprecated use
* {@link org.snakeyaml.engine.v2.api.LoadSettingsBuilder#setUseMarks(boolean)
* LoadSettingsBuilder#setUseMarks} with
* {@link #LOAD_CONFIG} to configure this option.
*/
public static final String LOAD_USE_MARKS = "LOAD_USE_MARKS";
@Deprecated(since = "1.0", forRemoval = true)
public static final String LOAD_USE_MARKS = "LOAD_USE_MARKS"; // NOSONAR

/**
* Set to true if the document start must be explicitly indicated by adding
* {@code ---} at the beginning of the document.
* Set to true if the document start must be explicitly indicated by
* adding {@code ---} at the beginning of the document.
*
* @see org.yaml.snakeyaml.DumperOptions#setExplicitStart(boolean)
* @see org.snakeyaml.engine.v2.api.DumpSettingsBuilder#setExplicitStart(boolean)
*
* @deprecated use
* {@link org.yaml.snakeyaml.DumperOptions#setExplicitStart(boolean)
* DumperOptions#setExplicitStart} or
* {@link org.snakeyaml.engine.v2.api.DumpSettingsBuilder#setExplicitStart(boolean)
* DumpSettingsBuilder#setExplicitStart} with
* {@link #DUMP_CONFIG} to configure this option.
*/
public static final String DUMP_EXPLICIT_START = "DUMP_EXPLICIT_START";
@Deprecated(since = "1.0", forRemoval = true)
public static final String DUMP_EXPLICIT_START = "DUMP_EXPLICIT_START"; // NOSONAR

/**
* Set to true if the document end must be explicitly indicated by
* adding {@code ...} at the end of the document.
*
* @see org.yaml.snakeyaml.DumperOptions#setExplicitEnd(boolean)
* @see org.snakeyaml.engine.v2.api.DumpSettingsBuilder#setExplicitEnd(boolean)
*
* @deprecated use
* {@link org.yaml.snakeyaml.DumperOptions#setExplicitEnd(boolean)
* DumperOptions#setExplicitEnd} or
* {@link org.snakeyaml.engine.v2.api.DumpSettingsBuilder#setExplicitEnd(boolean)
* DumpSettingsBuilder#setExplicitEnd} with
* {@link #DUMP_CONFIG} to configure this option.
*/
@Deprecated(since = "1.0", forRemoval = true)
public static final String DUMP_EXPLICIT_END = "DUMP_EXPLICIT_END"; // NOSONAR

/**
* Used to pass a pre-configured
* {@linkplain org.yaml.snakeyaml.LoaderOptions LoaderOptions} or
* {@linkplain org.snakeyaml.engine.v2.api.LoadSettings LoadSettings}
* instance for use when reading or parsing YAML.
*
* @since 1.0
*/
public static final String LOAD_CONFIG = "io.xlate.yamljson.LOAD_CONFIG";

/**
* Used to pass a pre-configured
* {@linkplain org.yaml.snakeyaml.DumperOptions DumperOptions} or
* {@linkplain org.snakeyaml.engine.v2.api.DumpSettings DumpSettings}
* instance for use when writing or generating YAML.
*
* @since 1.0
*/
public static final String DUMP_EXPLICIT_END = "DUMP_EXPLICIT_END";
public static final String DUMP_CONFIG = "io.xlate.yamljson.DUMP_CONFIG";

}

private static final YamlProvider PROVIDER = new YamlProvider();
Expand Down
39 changes: 14 additions & 25 deletions src/main/java/io/xlate/yamljson/YamlGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@ enum ContextType {
enum EventType {
STREAM_START,
STREAM_END,
DOCUMENT_START_DEFAULT,
DOCUMENT_START_EXPLICIT,
DOCUMENT_END_DEFAULT,
DOCUMENT_END_EXPLICIT,
DOCUMENT_START,
DOCUMENT_END,
MAPPING_START,
MAPPING_END,
SEQUENCE_START,
Expand All @@ -73,30 +71,23 @@ interface IOOperation {

static final StringQuotingChecker quoteChecker = new StringQuotingChecker();

protected final Map<EventType, E> eventTypes;
protected final Map<StyleType, S> styleTypes;
protected final Writer writer;
final boolean explicitStart;
final boolean explicitEnd;
final Deque<ContextType> context = new ArrayDeque<>();

YamlGenerator(Map<EventType, E> eventTypes, Map<StyleType, S> styleTypes, Writer writer, boolean explicitStart, boolean explicitEnd) {
this.eventTypes = eventTypes;
YamlGenerator(Map<StyleType, S> styleTypes, Writer writer) {
this.styleTypes = styleTypes;
this.writer = writer;
this.explicitStart = explicitStart;
this.explicitEnd = explicitEnd;
}

protected abstract E getEvent(EventType type);
protected abstract void emitEvent(E event) throws IOException;
protected abstract E buildScalarEvent(String scalarValue, S style);

void ensureDocumentStarted() {
if (context.isEmpty()) {
emit(eventTypes.get(EventType.STREAM_START));
emit(explicitStart ?
eventTypes.get(EventType.DOCUMENT_START_EXPLICIT) :
eventTypes.get(EventType.DOCUMENT_START_DEFAULT));
emit(getEvent(EventType.STREAM_START));
emit(getEvent(EventType.DOCUMENT_START));
}
}

Expand Down Expand Up @@ -168,7 +159,7 @@ protected static void execute(String name, IOOperation operation) {
public JsonGenerator writeStartObject() {
ensureDocumentStarted();
context.push(ContextType.OBJECT);
emit(eventTypes.get(EventType.MAPPING_START));
emit(getEvent(EventType.MAPPING_START));
return this;
}

Expand All @@ -177,7 +168,7 @@ public JsonGenerator writeStartObject(String name) {
Objects.requireNonNull(name, "name");
writeKey(name);
context.push(ContextType.OBJECT);
emit(eventTypes.get(EventType.MAPPING_START));
emit(getEvent(EventType.MAPPING_START));
return this;
}

Expand All @@ -193,7 +184,7 @@ public JsonGenerator writeKey(String name) {
public JsonGenerator writeStartArray() {
ensureDocumentStarted();
context.push(ContextType.ARRAY);
emit(eventTypes.get(EventType.SEQUENCE_START));
emit(getEvent(EventType.SEQUENCE_START));
return this;
}

Expand All @@ -202,7 +193,7 @@ public JsonGenerator writeStartArray(String name) {
Objects.requireNonNull(name, "name");
writeKey(name);
context.push(ContextType.ARRAY);
emit(eventTypes.get(EventType.SEQUENCE_START));
emit(getEvent(EventType.SEQUENCE_START));
return this;
}

Expand Down Expand Up @@ -269,16 +260,14 @@ public JsonGenerator writeEnd() {
ContextType contextType = this.context.pop();

if (contextType == ContextType.OBJECT) {
emit(eventTypes.get(EventType.MAPPING_END));
emit(getEvent(EventType.MAPPING_END));
} else {
emit(eventTypes.get(EventType.SEQUENCE_END));
emit(getEvent(EventType.SEQUENCE_END));
}

if (this.context.isEmpty()) {
emit(explicitEnd ?
eventTypes.get(EventType.DOCUMENT_END_EXPLICIT) :
eventTypes.get(EventType.DOCUMENT_END_DEFAULT));
emit(eventTypes.get(EventType.STREAM_END));
emit(getEvent(EventType.DOCUMENT_END));
emit(getEvent(EventType.STREAM_END));
}

return this;
Expand Down
26 changes: 18 additions & 8 deletions src/main/java/io/xlate/yamljson/YamlGeneratorFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,28 @@ static void copyBoolean(Map<String, Object> properties, String name, Consumer<Bo
target.accept(getProperty(properties, name, Boolean::valueOf, false));
}

@SuppressWarnings("removal")
static DumperOptions buildDumperOptions(Map<String, Object> properties) {
DumperOptions options = new DumperOptions();
copyBoolean(properties, Yaml.Settings.DUMP_EXPLICIT_START, options::setExplicitStart);
copyBoolean(properties, Yaml.Settings.DUMP_EXPLICIT_END, options::setExplicitEnd);
return options;
return Optional.ofNullable(properties.get(Yaml.Settings.DUMP_CONFIG))
.map(DumperOptions.class::cast)
.orElseGet(() -> {
DumperOptions options = new DumperOptions();
copyBoolean(properties, Yaml.Settings.DUMP_EXPLICIT_START, options::setExplicitStart);
copyBoolean(properties, Yaml.Settings.DUMP_EXPLICIT_END, options::setExplicitEnd);
return options;
});
}

@SuppressWarnings("removal")
static DumpSettings buildDumpSettings(Map<String, Object> properties) {
DumpSettingsBuilder settings = DumpSettings.builder();
copyBoolean(properties, Yaml.Settings.DUMP_EXPLICIT_START, settings::setExplicitStart);
copyBoolean(properties, Yaml.Settings.DUMP_EXPLICIT_END, settings::setExplicitEnd);
return settings.build();
return Optional.ofNullable(properties.get(Yaml.Settings.DUMP_CONFIG))
.map(DumpSettings.class::cast)
.orElseGet(() -> {
DumpSettingsBuilder settings = DumpSettings.builder();
copyBoolean(properties, Yaml.Settings.DUMP_EXPLICIT_START, settings::setExplicitStart);
copyBoolean(properties, Yaml.Settings.DUMP_EXPLICIT_END, settings::setExplicitEnd);
return settings.build();
});
}

private final Map<String, Object> properties;
Expand Down
17 changes: 13 additions & 4 deletions src/main/java/io/xlate/yamljson/YamlParserFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,24 @@ class YamlParserFactory implements JsonParserFactory, SettingsBuilder {

static final Function<Map<String, Object>, Object> SNAKEYAML_FACTORY =
// No load properties supported currently
props -> new org.yaml.snakeyaml.Yaml(new LoaderOptions());
props -> new org.yaml.snakeyaml.Yaml(buildLoaderOptions(props));

static final Function<Map<String, Object>, Object> SNAKEYAML_ENGINE_FACTORY =
props -> new org.snakeyaml.engine.v2.api.lowlevel.Parse(buildLoadSettings(props));

static LoaderOptions buildLoaderOptions(Map<String, Object> properties) {
return Optional.ofNullable(properties.get(Yaml.Settings.LOAD_CONFIG))
.map(LoaderOptions.class::cast)
.orElseGet(LoaderOptions::new);
}

@SuppressWarnings("removal")
static LoadSettings buildLoadSettings(Map<String, Object> properties) {
return LoadSettings.builder()
.setUseMarks(getProperty(properties, Yaml.Settings.LOAD_USE_MARKS, Boolean::valueOf, true))
.build();
return Optional.ofNullable(properties.get(Yaml.Settings.LOAD_CONFIG))
.map(LoadSettings.class::cast)
.orElseGet(() -> LoadSettings.builder()
.setUseMarks(getProperty(properties, Yaml.Settings.LOAD_USE_MARKS, Boolean::valueOf, true))
.build());
}

private final Map<String, Object> properties;
Expand Down

0 comments on commit 4daadd1

Please sign in to comment.