org.snakeyaml.engine.v2.serializer.Serializer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of snakeyaml-engine Show documentation
Show all versions of snakeyaml-engine Show documentation
Core YAML 1.2 parser and emitter for Java
The newest version!
/*
* Copyright (c) 2018, SnakeYAML
*
* 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 org.snakeyaml.engine.v2.serializer;
import org.snakeyaml.engine.v2.api.DumpSettings;
import org.snakeyaml.engine.v2.comments.CommentLine;
import org.snakeyaml.engine.v2.common.Anchor;
import org.snakeyaml.engine.v2.emitter.Emitable;
import org.snakeyaml.engine.v2.events.AliasEvent;
import org.snakeyaml.engine.v2.events.CommentEvent;
import org.snakeyaml.engine.v2.events.DocumentEndEvent;
import org.snakeyaml.engine.v2.events.DocumentStartEvent;
import org.snakeyaml.engine.v2.events.Event;
import org.snakeyaml.engine.v2.events.ImplicitTuple;
import org.snakeyaml.engine.v2.events.MappingEndEvent;
import org.snakeyaml.engine.v2.events.MappingStartEvent;
import org.snakeyaml.engine.v2.events.ScalarEvent;
import org.snakeyaml.engine.v2.events.SequenceEndEvent;
import org.snakeyaml.engine.v2.events.SequenceStartEvent;
import org.snakeyaml.engine.v2.events.StreamEndEvent;
import org.snakeyaml.engine.v2.events.StreamStartEvent;
import org.snakeyaml.engine.v2.exceptions.YamlEngineException;
import org.snakeyaml.engine.v2.nodes.AnchorNode;
import org.snakeyaml.engine.v2.nodes.MappingNode;
import org.snakeyaml.engine.v2.nodes.Node;
import org.snakeyaml.engine.v2.nodes.NodeTuple;
import org.snakeyaml.engine.v2.nodes.NodeType;
import org.snakeyaml.engine.v2.nodes.ScalarNode;
import org.snakeyaml.engine.v2.nodes.SequenceNode;
import org.snakeyaml.engine.v2.nodes.Tag;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* Transform a Node Graph to Event stream and allow provided {@link Emitable} to present the
* {@link Event}s into the output stream
*/
public class Serializer {
private final DumpSettings settings;
private final Emitable emitable;
private final Set serializedNodes;
private final Map anchors;
private final boolean dereferenceAliases;
private final Set recursive;
/**
* Create Serializer
*
* @param settings - dump configuration
* @param emitable - destination for the event stream
*/
public Serializer(DumpSettings settings, Emitable emitable) {
this.settings = settings;
this.emitable = emitable;
this.serializedNodes = new HashSet<>();
this.anchors = new HashMap<>();
this.dereferenceAliases = settings.isDereferenceAliases();
this.recursive = Collections.newSetFromMap(new IdentityHashMap());
}
/**
* Serialize document
*
* @param node - the document root
*/
public void serializeDocument(Node node) {
this.emitable.emit(new DocumentStartEvent(settings.isExplicitStart(),
settings.getYamlDirective(), settings.getTagDirective()));
anchorNode(node);
settings.getExplicitRootTag().ifPresent(node::setTag);
serializeNode(node);
this.emitable.emit(new DocumentEndEvent(settings.isExplicitEnd()));
this.serializedNodes.clear();
this.anchors.clear();
this.recursive.clear();
}
/**
* Emit {@link StreamStartEvent}
*/
public void emitStreamStart() {
this.emitable.emit(new StreamStartEvent());
}
/**
* Emit {@link StreamEndEvent}
*/
public void emitStreamEnd() {
this.emitable.emit(new StreamEndEvent());
}
private void anchorNode(Node node) {
final Node realNode;
if (node.getNodeType() == NodeType.ANCHOR) {
realNode = ((AnchorNode) node).getRealNode();
} else {
realNode = node;
}
if (this.anchors.containsKey(realNode)) {
// it looks weird, anchor does contain the key node, but we call computeIfAbsent()
// this is because the value is null (HashMap permits values to be null)
this.anchors.computeIfAbsent(realNode,
a -> settings.getAnchorGenerator().nextAnchor(realNode));
} else {
this.anchors.put(realNode,
realNode.getAnchor().isPresent() ? settings.getAnchorGenerator().nextAnchor(realNode)
: null);
switch (realNode.getNodeType()) {
case SEQUENCE:
SequenceNode seqNode = (SequenceNode) realNode;
List list = seqNode.getValue();
for (Node item : list) {
anchorNode(item);
}
break;
case MAPPING:
MappingNode mappingNode = (MappingNode) realNode;
List map = mappingNode.getValue();
for (NodeTuple object : map) {
Node key = object.getKeyNode();
Node value = object.getValueNode();
anchorNode(key);
anchorNode(value);
}
break;
default: // no further action required for non-collections
}
}
}
/**
* Recursive serialization of a {@link Node}
*
* @param node - content
*/
private void serializeNode(Node node) {
if (node.getNodeType() == NodeType.ANCHOR) {
node = ((AnchorNode) node).getRealNode();
}
if (dereferenceAliases && recursive.contains(node)) {
throw new YamlEngineException("Cannot dereferenceAliases for recursive structures.");
}
recursive.add(node);
Optional tAlias;
if (!dereferenceAliases) {
tAlias = Optional.ofNullable(this.anchors.get(node));
} else {
tAlias = Optional.empty();
}
if (!dereferenceAliases && this.serializedNodes.contains(node)) {
this.emitable.emit(new AliasEvent(tAlias));
} else {
this.serializedNodes.add(node);
switch (node.getNodeType()) {
case SCALAR:
ScalarNode scalarNode = (ScalarNode) node;
serializeComments(node.getBlockComments());
Tag detectedTag =
settings.getSchema().getScalarResolver().resolve(scalarNode.getValue(), true);
Tag defaultTag =
settings.getSchema().getScalarResolver().resolve(scalarNode.getValue(), false);
ImplicitTuple tuple = new ImplicitTuple(node.getTag().equals(detectedTag),
node.getTag().equals(defaultTag));
ScalarEvent event = new ScalarEvent(tAlias, Optional.of(node.getTag().getValue()), tuple,
scalarNode.getValue(), scalarNode.getScalarStyle());
this.emitable.emit(event);
serializeComments(node.getInLineComments());
serializeComments(node.getEndComments());
break;
case SEQUENCE:
SequenceNode seqNode = (SequenceNode) node;
serializeComments(node.getBlockComments());
boolean implicitS = node.getTag().equals(Tag.SEQ);
this.emitable.emit(new SequenceStartEvent(tAlias, Optional.of(node.getTag().getValue()),
implicitS, seqNode.getFlowStyle()));
List list = seqNode.getValue();
for (Node item : list) {
serializeNode(item);
}
this.emitable.emit(new SequenceEndEvent());
serializeComments(node.getInLineComments());
serializeComments(node.getEndComments());
break;
default:// instance of MappingNode
serializeComments(node.getBlockComments());
boolean implicitM = node.getTag().equals(Tag.MAP);
MappingNode mappingNode = (MappingNode) node;
List map = mappingNode.getValue();
if (mappingNode.getTag() != Tag.COMMENT) {
this.emitable
.emit(new MappingStartEvent(tAlias, Optional.of(mappingNode.getTag().getValue()),
implicitM, mappingNode.getFlowStyle(), Optional.empty(), Optional.empty()));
for (NodeTuple entry : map) {
Node key = entry.getKeyNode();
Node value = entry.getValueNode();
serializeNode(key);
serializeNode(value);
}
this.emitable.emit(new MappingEndEvent());
serializeComments(node.getInLineComments());
serializeComments(node.getEndComments());
}
}
}
recursive.remove(node);
}
private void serializeComments(List comments) {
if (settings.getDumpComments() && comments != null) {
for (CommentLine line : comments) {
CommentEvent commentEvent = new CommentEvent(line.getCommentType(), line.getValue(),
line.getStartMark(), line.getEndMark());
this.emitable.emit(commentEvent);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy