aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmer Azmon <omer_azmon@intuit.com>2020-10-13 19:57:50 -0700
committerOmer Azmon <omer_azmon@intuit.com>2020-10-13 19:57:50 -0700
commitec339568abd68994804d763327a05b29f88002fd (patch)
tree6f974cfc068f40045d49f5c4c06d07bb97e50c5c
parentdea399176c2d1fee919cb671600d2b244c94f503 (diff)
downloadsnakeyaml-ec339568abd68994804d763327a05b29f88002fd.tar.gz
implement serializer and emitter comment changes required changes to
prior commits
-rw-r--r--src/main/java/org/yaml/snakeyaml/LoaderOptions.java14
-rw-r--r--src/main/java/org/yaml/snakeyaml/Yaml.java6
-rw-r--r--src/main/java/org/yaml/snakeyaml/comments/CommentEventsCollector.java180
-rw-r--r--src/main/java/org/yaml/snakeyaml/comments/CommentLine.java87
-rw-r--r--src/main/java/org/yaml/snakeyaml/comments/CommentType.java25
-rw-r--r--src/main/java/org/yaml/snakeyaml/composer/Composer.java98
-rw-r--r--src/main/java/org/yaml/snakeyaml/emitter/Emitter.java183
-rw-r--r--src/main/java/org/yaml/snakeyaml/events/CommentEvent.java17
-rw-r--r--src/main/java/org/yaml/snakeyaml/nodes/Node.java79
-rw-r--r--src/main/java/org/yaml/snakeyaml/nodes/Tag.java11
-rw-r--r--src/main/java/org/yaml/snakeyaml/parser/ParserImpl.java84
-rw-r--r--src/main/java/org/yaml/snakeyaml/scanner/ScannerImpl.java108
-rw-r--r--src/main/java/org/yaml/snakeyaml/serializer/Serializer.java43
-rw-r--r--src/main/java/org/yaml/snakeyaml/tokens/CommentToken.java7
-rw-r--r--src/test/java/org/yaml/snakeyaml/comment/ComposerWithCommentEnabledTest.java528
-rw-r--r--src/test/java/org/yaml/snakeyaml/comment/EmitterWithCommentEnabledTest.java213
-rw-r--r--src/test/java/org/yaml/snakeyaml/comment/ParserWithCommentEnabledTest.java180
-rw-r--r--src/test/java/org/yaml/snakeyaml/comment/SerializerWithCommentEnabledTest.java389
-rw-r--r--src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java4
-rw-r--r--src/test/java/org/yaml/snakeyaml/emitter/EmptyStringOutputTest.java3
20 files changed, 2060 insertions, 199 deletions
diff --git a/src/main/java/org/yaml/snakeyaml/LoaderOptions.java b/src/main/java/org/yaml/snakeyaml/LoaderOptions.java
index b23bbee0..724893cb 100644
--- a/src/main/java/org/yaml/snakeyaml/LoaderOptions.java
+++ b/src/main/java/org/yaml/snakeyaml/LoaderOptions.java
@@ -21,6 +21,7 @@ public class LoaderOptions {
private boolean wrappedToRootException = false;
private int maxAliasesForCollections = 50; //to prevent YAML at https://en.wikipedia.org/wiki/Billion_laughs_attack
private boolean allowRecursiveKeys = false;
+ private boolean processComments = false;
public boolean isAllowDuplicateKeys() {
return allowDuplicateKeys;
@@ -85,4 +86,17 @@ public class LoaderOptions {
public boolean getAllowRecursiveKeys() {
return allowRecursiveKeys;
}
+
+ /**
+ * Set the comment processing. By default comments are ignored.
+ *
+ * @param processComments <code>true</code> to process; <code>false</code> to ignore</code>
+ */
+ public void setProcessComments(boolean processComments) {
+ this.processComments = processComments;
+ }
+
+ public boolean isProcessComments() {
+ return processComments;
+ }
}
diff --git a/src/main/java/org/yaml/snakeyaml/Yaml.java b/src/main/java/org/yaml/snakeyaml/Yaml.java
index 64f68a25..e52bb6e5 100644
--- a/src/main/java/org/yaml/snakeyaml/Yaml.java
+++ b/src/main/java/org/yaml/snakeyaml/Yaml.java
@@ -568,7 +568,8 @@ public class Yaml {
* Overview</a>
*/
public Node compose(Reader yaml) {
- Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver, loadingConfig);
+ Composer composer = new Composer(new ParserImpl(new StreamReader(yaml),
+ loadingConfig.isProcessComments()), resolver, loadingConfig);
return composer.getSingleNode();
}
@@ -581,7 +582,8 @@ public class Yaml {
* @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a>
*/
public Iterable<Node> composeAll(Reader yaml) {
- final Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver, loadingConfig);
+ final Composer composer = new Composer(new ParserImpl(new StreamReader(yaml),
+ loadingConfig.isProcessComments()), resolver, loadingConfig);
Iterator<Node> result = new Iterator<Node>() {
@Override
public boolean hasNext() {
diff --git a/src/main/java/org/yaml/snakeyaml/comments/CommentEventsCollector.java b/src/main/java/org/yaml/snakeyaml/comments/CommentEventsCollector.java
new file mode 100644
index 00000000..b5ed5349
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/comments/CommentEventsCollector.java
@@ -0,0 +1,180 @@
+/**
+ * Copyright (c) 2020, http://www.snakeyaml.org
+ *
+ * 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.yaml.snakeyaml.comments;
+
+import java.util.AbstractQueue;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Queue;
+
+import org.yaml.snakeyaml.events.CommentEvent;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.parser.Parser;
+
+/**
+ * Used by the Composer and Emitter to collect comment events so that they can be used at a later point in the process.
+ */
+public class CommentEventsCollector {
+ private List<CommentLine> commentLineList;
+ private Queue<Event> eventSource;
+ private CommentType[] expectedCommentTypes;
+
+ /**
+ * Constructor used to collect comment events emitted by a Parser.
+ *
+ * @param parser
+ * the event source.
+ * @param expectedCommentTypes
+ * the comment types expected. Any comment types not included are not collected.
+ */
+ public CommentEventsCollector(final Parser parser, CommentType... expectedCommentTypes) {
+ this.eventSource = new AbstractQueue<Event>() {
+
+ @Override
+ public boolean offer(Event e) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Event poll() {
+ return parser.getEvent();
+ }
+
+ @Override
+ public Event peek() {
+ return parser.peekEvent();
+ }
+
+ @Override
+ public Iterator<Event> iterator() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int size() {
+ throw new UnsupportedOperationException();
+ }
+
+ };
+ this.expectedCommentTypes = expectedCommentTypes;
+ commentLineList = new ArrayList<>();
+ }
+
+ /**
+ * Constructor used to collect events emitted by the Serializer.
+ *
+ * @param eventSource
+ * the event source.
+ *
+ * @param expectedCommentTypes
+ * the comment types expected. Any comment types not included are not collected.
+ */
+ public CommentEventsCollector(Queue<Event> eventSource, CommentType... expectedCommentTypes) {
+ this.eventSource = eventSource;
+ this.expectedCommentTypes = expectedCommentTypes;
+ commentLineList = new ArrayList<>();
+ }
+
+ /**
+ * Determine if the event is a comment of one of the expected types set during construction.
+ *
+ * @param event
+ * the event to test.
+ * @return <code>true</code> if the events is a comment of the expected type; Otherwise, false.
+ */
+ private boolean isEventExpected(Event event) {
+ if (event == null || !event.is(Event.ID.Comment)) {
+ return false;
+ }
+ CommentEvent commentEvent = (CommentEvent) event;
+ for (CommentType type : expectedCommentTypes) {
+ if (commentEvent.getCommentType() == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Collect all events of the expected type (set during construction) starting with the top event on the event source.
+ * Collection stops as soon as a non comment or comment of the unexpected type is encountered.
+ *
+ * @return this object.
+ */
+ public CommentEventsCollector collectEvents() {
+ collectEvents(null);
+ return this;
+ }
+
+ /**
+ * Collect all events of the expected type (set during construction) starting with event provided as an argument and
+ * continuing with the top event on the event source. Collection stops as soon as a non comment or comment of the
+ * unexpected type is encountered.
+ *
+ * @param event
+ * the first event to attempt to collect.
+ * @return the event provided as an argument, if it is not collected; Otherwise, <code>null</code>
+ */
+ public Event collectEvents(Event event) {
+ if (event != null) {
+ if (isEventExpected(event)) {
+ commentLineList.add(new CommentLine((CommentEvent) event));
+ } else {
+ return event;
+ }
+ }
+ while (isEventExpected(eventSource.peek())) {
+ commentLineList.add(new CommentLine((CommentEvent) eventSource.poll()));
+ }
+ return null;
+ }
+
+ /**
+ * Collect all events of the expected type (set during construction) starting with event provided as an argument and
+ * continuing with the top event on the event source. Collection stops as soon as a non comment or comment of the
+ * unexpected type is encountered.
+ *
+ * @param event
+ * the first event to attempt to collect.
+ * @return the event provided as an argument, if it is not collected; Otherwise, the first event that is not collected.
+ */
+ public Event collectEventsAndPoll(Event event) {
+ Event nextEvent = collectEvents(event);
+ return nextEvent != null ? nextEvent : eventSource.poll();
+ }
+
+ /**
+ * Return the events collected and reset the colletor.
+ *
+ * @return the events collected.
+ */
+ public List<CommentLine> consume() {
+ try {
+ return commentLineList;
+ } finally {
+ commentLineList = new ArrayList<>();
+ }
+ }
+
+ /**
+ * Test if the collector contains any collected events.
+ * @return <code>true</code> if it does; Otherwise, <code>false</code>
+ */
+ public boolean isEmpty() {
+ return commentLineList.isEmpty();
+ }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/comments/CommentLine.java b/src/main/java/org/yaml/snakeyaml/comments/CommentLine.java
new file mode 100644
index 00000000..63732cc0
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/comments/CommentLine.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2020, http://www.snakeyaml.org
+ *
+ * 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.yaml.snakeyaml.comments;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.events.CommentEvent;
+
+/**
+ * A comment line. May be a block comment, blank line, or inline comment.
+ */
+public class CommentLine {
+ private Mark startMark;
+ private Mark endMark;
+ private String value;
+ private CommentType commentType;
+
+ public CommentLine(CommentEvent event) {
+ this(event.getStartMark(), event.getEndMark(), event.getValue(), event.getCommentType());
+ }
+
+ public CommentLine(Mark startMark, Mark endMark, String value, CommentType commentType) {
+ this.startMark = startMark;
+ this.endMark = endMark;
+ this.value = value;
+ this.commentType = commentType;
+ }
+
+ /**
+ * CommentLine is only equal to itself
+ */
+ @Override
+ public final boolean equals(Object obj) {
+ return super.equals(obj);
+ }
+
+ @Override
+ public final int hashCode() {
+ return super.hashCode();
+ }
+
+ public Mark getEndMark() {
+ return endMark;
+ }
+
+ public Mark getStartMark() {
+ return startMark;
+ }
+
+ /**
+ * Is this comment blank lines or a regular comment (starts with '#').
+ *
+ * @return <code>true</code> if blank lines; Otherwise, <code>false</code>.
+ */
+ public boolean isBlankLines() {
+ return commentType == CommentType.BLANK_LINE;
+ }
+
+ public CommentType getCommentType() {
+ return commentType;
+ }
+
+ /**
+ * Value of this comment.
+ *
+ * @return comment's value.
+ */
+ public String getValue() {
+ return value;
+ }
+
+ public String toString() {
+ return "<" + this.getClass().getName() + " (type=" + getCommentType() + ", value=" + getValue() + ")>";
+ }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/comments/CommentType.java b/src/main/java/org/yaml/snakeyaml/comments/CommentType.java
new file mode 100644
index 00000000..2abc9a88
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/comments/CommentType.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2020, http://www.snakeyaml.org
+ *
+ * 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.yaml.snakeyaml.comments;
+
+/**
+ * The type of a comment line.
+ */
+public enum CommentType {
+ BLANK_LINE, //
+ BLOCK, //
+ IN_LINE; //
+} \ No newline at end of file
diff --git a/src/main/java/org/yaml/snakeyaml/composer/Composer.java b/src/main/java/org/yaml/snakeyaml/composer/Composer.java
index 964391ac..2135d84f 100644
--- a/src/main/java/org/yaml/snakeyaml/composer/Composer.java
+++ b/src/main/java/org/yaml/snakeyaml/composer/Composer.java
@@ -16,13 +16,18 @@
package org.yaml.snakeyaml.composer;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.LoaderOptions;
+import org.yaml.snakeyaml.comments.CommentEventsCollector;
+import org.yaml.snakeyaml.comments.CommentLine;
+import org.yaml.snakeyaml.comments.CommentType;
import org.yaml.snakeyaml.error.Mark;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.events.AliasEvent;
@@ -55,6 +60,8 @@ public class Composer {
private final Set<Node> recursiveNodes;
private int nonScalarAliasesCount = 0;
private final LoaderOptions loadingConfig;
+ private final CommentEventsCollector blockCommentsCollector;
+ private final CommentEventsCollector inlineCommentsCollector;
public Composer(Parser parser, Resolver resolver) {
this(parser, resolver, new LoaderOptions());
@@ -66,6 +73,10 @@ public class Composer {
this.anchors = new HashMap<String, Node>();
this.recursiveNodes = new HashSet<Node>();
this.loadingConfig = loadingConfig;
+ this.blockCommentsCollector = new CommentEventsCollector(parser,
+ CommentType.BLANK_LINE, CommentType.BLOCK);
+ this.inlineCommentsCollector = new CommentEventsCollector(parser,
+ CommentType.IN_LINE);
}
/**
@@ -85,17 +96,29 @@ public class Composer {
/**
* Reads and composes the next document.
*
- * @return The root node of the document or <code>null</code> if no more
- * documents are available.
+ * @return The root node of the document or <code>null</code> if no more documents are available.
*/
public Node getNode() {
+ // Collect inter-document start comments
+ blockCommentsCollector.collectEvents();
+ if (parser.checkEvent(Event.ID.StreamEnd)) {
+ List<CommentLine> commentLines = blockCommentsCollector.consume();
+ Mark startMark = commentLines.get(0).getStartMark();
+ List<NodeTuple> children = Collections.emptyList();
+ Node node = new MappingNode(Tag.COMMENT, false, children, startMark, null, FlowStyle.BLOCK);
+ node.setBlockComments(commentLines);
+ return node;
+ }
// Drop the DOCUMENT-START event.
parser.getEvent();
// Compose the root node.
- Node node = composeNode(null);
+ Node node = composeNode(null, blockCommentsCollector.collectEvents().consume());
// Drop the DOCUMENT-END event.
+ blockCommentsCollector.collectEvents();
+ if(!blockCommentsCollector.isEmpty()) {
+ node.setEndComments(blockCommentsCollector.consume());
+ }
parser.getEvent();
- //clean up resources
this.anchors.clear();
this.recursiveNodes.clear();
return node;
@@ -113,11 +136,19 @@ public class Composer {
public Node getSingleNode() {
// Drop the STREAM-START event.
parser.getEvent();
+ // Drop any leading comments (though should not have run this case with comments on)
+ while (parser.checkEvent(Event.ID.Comment)) {
+ parser.getEvent();
+ }
// Compose a document if the stream is not empty.
Node document = null;
if (!parser.checkEvent(Event.ID.StreamEnd)) {
document = getNode();
}
+ // Drop any trailing comments (though should not have run this case with comments on)
+ while (parser.checkEvent(Event.ID.Comment)) {
+ parser.getEvent();
+ }
// Ensure that the stream contains no more documents.
if (!parser.checkEvent(Event.ID.StreamEnd)) {
Event event = parser.getEvent();
@@ -130,8 +161,9 @@ public class Composer {
return document;
}
- private Node composeNode(Node parent) {
- if (parent != null) recursiveNodes.add(parent);
+ private Node composeNode(Node parent, List<CommentLine> blockComments) {
+ if (parent != null)
+ recursiveNodes.add(parent);
final Node node;
if (parser.checkEvent(Event.ID.Alias)) {
AliasEvent event = (AliasEvent) parser.getEvent();
@@ -150,23 +182,24 @@ public class Composer {
if (recursiveNodes.remove(node)) {
node.setTwoStepsConstruction(true);
}
+ node.setBlockComments(blockComments);
} else {
NodeEvent event = (NodeEvent) parser.peekEvent();
String anchor = event.getAnchor();
// the check for duplicate anchors has been removed (issue 174)
if (parser.checkEvent(Event.ID.Scalar)) {
- node = composeScalarNode(anchor);
+ node = composeScalarNode(anchor, blockComments);
} else if (parser.checkEvent(Event.ID.SequenceStart)) {
- node = composeSequenceNode(anchor);
+ node = composeSequenceNode(anchor, blockComments);
} else {
- node = composeMappingNode(anchor);
+ node = composeMappingNode(anchor, blockComments);
}
}
recursiveNodes.remove(parent);
return node;
}
- protected Node composeScalarNode(String anchor) {
+ protected Node composeScalarNode(String anchor, List<CommentLine> blockComments) {
ScalarEvent ev = (ScalarEvent) parser.getEvent();
String tag = ev.getTag();
boolean resolved = false;
@@ -184,13 +217,16 @@ public class Composer {
node.setAnchor(anchor);
anchors.put(anchor, node);
}
+ node.setBlockComments(blockComments);
+ node.setInLineComments(inlineCommentsCollector.collectEvents().consume());
return node;
}
- protected Node composeSequenceNode(String anchor) {
+ protected Node composeSequenceNode(String anchor, List<CommentLine> blockComments) {
SequenceStartEvent startEvent = (SequenceStartEvent) parser.getEvent();
String tag = startEvent.getTag();
Tag nodeTag;
+
boolean resolved = false;
if (tag == null || tag.equals("!")) {
nodeTag = resolver.resolve(NodeId.sequence, null, startEvent.getImplicit());
@@ -205,15 +241,25 @@ public class Composer {
node.setAnchor(anchor);
anchors.put(anchor, node);
}
+ node.setBlockComments(blockComments);
+ node.setInLineComments(inlineCommentsCollector.collectEvents().consume());
while (!parser.checkEvent(Event.ID.SequenceEnd)) {
- children.add(composeNode(node));
+ blockCommentsCollector.collectEvents();
+ if (parser.checkEvent(Event.ID.SequenceEnd)) {
+ break;
+ }
+ children.add(composeNode(node, blockCommentsCollector.consume()));
}
Event endEvent = parser.getEvent();
node.setEndMark(endEvent.getEndMark());
+ inlineCommentsCollector.collectEvents();
+ if(!inlineCommentsCollector.isEmpty()) {
+ node.setInLineComments(inlineCommentsCollector.consume());
+ }
return node;
}
- protected Node composeMappingNode(String anchor) {
+ protected Node composeMappingNode(String anchor, List<CommentLine> blockComments) {
MappingStartEvent startEvent = (MappingStartEvent) parser.getEvent();
String tag = startEvent.getTag();
Tag nodeTag;
@@ -232,28 +278,38 @@ public class Composer {
node.setAnchor(anchor);
anchors.put(anchor, node);
}
+ node.setBlockComments(blockComments);
+ node.setInLineComments(inlineCommentsCollector.collectEvents().consume());
while (!parser.checkEvent(Event.ID.MappingEnd)) {
- composeMappingChildren(children, node);
+ blockCommentsCollector.collectEvents();
+ if (parser.checkEvent(Event.ID.MappingEnd)) {
+ break;
+ }
+ composeMappingChildren(children, node, blockCommentsCollector.consume());
}
Event endEvent = parser.getEvent();
node.setEndMark(endEvent.getEndMark());
+ inlineCommentsCollector.collectEvents();
+ if(!inlineCommentsCollector.isEmpty()) {
+ node.setInLineComments(inlineCommentsCollector.consume());
+ }
return node;
}
- protected void composeMappingChildren(List<NodeTuple> children, MappingNode node) {
- Node itemKey = composeKeyNode(node);
+ protected void composeMappingChildren(List<NodeTuple> children, MappingNode node, List<CommentLine> keyBlockComments) {
+ Node itemKey = composeKeyNode(node, keyBlockComments);
if (itemKey.getTag().equals(Tag.MERGE)) {
node.setMerged(true);
}
- Node itemValue = composeValueNode(node);
+ Node itemValue = composeValueNode(node, blockCommentsCollector.collectEvents().consume());
children.add(new NodeTuple(itemKey, itemValue));
}
- protected Node composeKeyNode(MappingNode node) {
- return composeNode(node);
+ protected Node composeKeyNode(MappingNode node, List<CommentLine> blockComments) {
+ return composeNode(node, blockComments);
}
- protected Node composeValueNode(MappingNode node) {
- return composeNode(node);
+ protected Node composeValueNode(MappingNode node, List<CommentLine> blockComments) {
+ return composeNode(node, blockComments);
}
}
diff --git a/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
index 4caad750..c060a7cb 100644
--- a/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
+++ b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
@@ -16,14 +16,20 @@
package org.yaml.snakeyaml.emitter;
import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
import org.yaml.snakeyaml.DumperOptions.Version;
+import org.yaml.snakeyaml.comments.CommentEventsCollector;
+import org.yaml.snakeyaml.comments.CommentLine;
+import org.yaml.snakeyaml.comments.CommentType;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.events.AliasEvent;
import org.yaml.snakeyaml.events.CollectionEndEvent;
import org.yaml.snakeyaml.events.CollectionStartEvent;
+import org.yaml.snakeyaml.events.CommentEvent;
import org.yaml.snakeyaml.events.DocumentEndEvent;
import org.yaml.snakeyaml.events.DocumentStartEvent;
import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.Event.ID;
import org.yaml.snakeyaml.events.MappingEndEvent;
import org.yaml.snakeyaml.events.MappingStartEvent;
import org.yaml.snakeyaml.events.NodeEvent;
@@ -43,6 +49,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
@@ -165,6 +172,11 @@ public final class Emitter implements Emitable {
// Scalar analysis and style.
private ScalarAnalysis analysis;
private DumperOptions.ScalarStyle style;
+
+ // Comment processing
+ private final CommentEventsCollector blockCommentsCollector;
+ private final CommentEventsCollector inlineCommentsCollector;
+
public Emitter(Writer stream, DumperOptions opts) {
// The stream should have the methods `write` and possibly `flush`.
@@ -226,6 +238,12 @@ public final class Emitter implements Emitable {
// Scalar analysis and style.
this.analysis = null;
this.style = null;
+
+ // Comment processing
+ this.blockCommentsCollector = new CommentEventsCollector(events,
+ CommentType.BLANK_LINE, CommentType.BLOCK);
+ this.inlineCommentsCollector = new CommentEventsCollector(events,
+ CommentType.IN_LINE);
}
public void emit(Event event) throws IOException {
@@ -243,36 +261,53 @@ public final class Emitter implements Emitable {
if (events.isEmpty()) {
return true;
}
- Event event = events.peek();
- if (event instanceof DocumentStartEvent) {
- return needEvents(1);
- } else if (event instanceof SequenceStartEvent) {
- return needEvents(2);
- } else if (event instanceof MappingStartEvent) {
- return needEvents(3);
- } else {
- return false;
+ Iterator<Event> iter = events.iterator();
+ Event event = null;
+ while(iter.hasNext()) {
+ event = iter.next();
+ if (event instanceof CommentEvent) {
+ continue;
+ }
+ if (event instanceof DocumentStartEvent) {
+ return needEvents(iter, 1);
+ } else if (event instanceof SequenceStartEvent) {
+ return needEvents(iter, 2);
+ } else if (event instanceof MappingStartEvent) {
+ return needEvents(iter, 3);
+ } else if (event instanceof StreamStartEvent) {
+ return needEvents(iter, 2);
+ } else if (event instanceof StreamEndEvent) {
+ return false;
+ } else {
+ // To collect any comment events
+ return needEvents(iter, 1);
+ }
}
+ return true;
}
-
- private boolean needEvents(int count) {
+
+ private boolean needEvents(Iterator<Event> iter, int count) {
int level = 0;
- Iterator<Event> iter = events.iterator();
- iter.next();
+ int actualCount = 0;
while (iter.hasNext()) {
Event event = iter.next();
+ if (event instanceof CommentEvent) {
+ continue;
+ }
+ actualCount++;
if (event instanceof DocumentStartEvent || event instanceof CollectionStartEvent) {
level++;
} else if (event instanceof DocumentEndEvent || event instanceof CollectionEndEvent) {
level--;
} else if (event instanceof StreamEndEvent) {
level = -1;
+ } else if (event instanceof CommentEvent) {
}
if (level < 0) {
return false;
}
}
- return events.size() < count + 1;
+ return actualCount < count;
}
private void increaseIndent(boolean flow, boolean indentless) {
@@ -366,6 +401,10 @@ public final class Emitter implements Emitable {
// }
writeStreamEnd();
state = new ExpectNothing();
+ } else if (event instanceof CommentEvent) {
+ blockCommentsCollector.collectEvents(event);
+ writeBlockComment();
+ // state = state; remains unchanged
} else {
throw new EmitterException("expected DocumentStartEvent, but got " + event);
}
@@ -374,6 +413,8 @@ public final class Emitter implements Emitable {
private class ExpectDocumentEnd implements EmitterState {
public void expect() throws IOException {
+ event = blockCommentsCollector.collectEventsAndPoll(event);
+ writeBlockComment();
if (event instanceof DocumentEndEvent) {
writeIndent();
if (((DocumentEndEvent) event).getExplicit()) {
@@ -390,6 +431,14 @@ public final class Emitter implements Emitable {
private class ExpectDocumentRoot implements EmitterState {
public void expect() throws IOException {
+ event = blockCommentsCollector.collectEventsAndPoll(event);
+ if (!blockCommentsCollector.isEmpty()) {
+ writeBlockComment();
+ if (event instanceof DocumentEndEvent) {
+ new ExpectDocumentEnd().expect();
+ return;
+ }
+ }
states.push(new ExpectDocumentEnd());
expectNode(true, false, false);
}
@@ -461,13 +510,20 @@ public final class Emitter implements Emitable {
indent = indents.pop();
flowLevel--;
writeIndicator("]", false, false, false);
+ inlineCommentsCollector.collectEvents();
+ writeInlineComments();
state = states.pop();
+ } else if (event instanceof CommentEvent) {
+ blockCommentsCollector.collectEvents(event);
+ writeBlockComment();
} else {
if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
writeIndent();
}
states.push(new ExpectFlowSequenceItem());
expectNode(false, false, false);
+ event = inlineCommentsCollector.collectEvents(event);
+ writeInlineComments();
}
}
}
@@ -482,10 +538,15 @@ public final class Emitter implements Emitable {
writeIndent();
}
writeIndicator("]", false, false, false);
+ inlineCommentsCollector.collectEvents();
+ writeInlineComments();
if (prettyFlow) {
writeIndent();
}
state = states.pop();
+ } else if (event instanceof CommentEvent) {
+ blockCommentsCollector.collectEvents(event);
+ writeBlockComment();
} else {
writeIndicator(",", false, false, false);
if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
@@ -493,6 +554,8 @@ public final class Emitter implements Emitable {
}
states.push(new ExpectFlowSequenceItem());
expectNode(false, false, false);
+ inlineCommentsCollector.collectEvents(event);
+ writeInlineComments();
}
}
}
@@ -515,6 +578,8 @@ public final class Emitter implements Emitable {
indent = indents.pop();
flowLevel--;
writeIndicator("}", false, false, false);
+ inlineCommentsCollector.collectEvents();
+ writeInlineComments();
state = states.pop();
} else {
if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
@@ -545,6 +610,8 @@ public final class Emitter implements Emitable {
writeIndent();
}
writeIndicator("}", false, false, false);
+ inlineCommentsCollector.collectEvents();
+ writeInlineComments();
state = states.pop();
} else {
writeIndicator(",", false, false, false);
@@ -566,8 +633,12 @@ public final class Emitter implements Emitable {
private class ExpectFlowMappingSimpleValue implements EmitterState {
public void expect() throws IOException {
writeIndicator(":", false, false, false);
+ event = inlineCommentsCollector.collectEventsAndPoll(event);
+ writeInlineComments();
states.push(new ExpectFlowMappingKey());
expectNode(false, true, false);
+ inlineCommentsCollector.collectEvents(event);
+ writeInlineComments();
}
}
@@ -577,8 +648,12 @@ public final class Emitter implements Emitable {
writeIndent();
}
writeIndicator(":", true, false, false);
+ event = inlineCommentsCollector.collectEventsAndPoll(event);
+ writeInlineComments();
states.push(new ExpectFlowMappingKey());
expectNode(false, true, false);
+ inlineCommentsCollector.collectEvents(event);
+ writeInlineComments();
}
}
@@ -607,6 +682,9 @@ public final class Emitter implements Emitable {
if (!this.first && event instanceof SequenceEndEvent) {
indent = indents.pop();
state = states.pop();
+ } else if( event instanceof CommentEvent) {
+ blockCommentsCollector.collectEvents(event);
+ writeBlockComment();
} else {
writeIndent();
if (!indentWithIndicator || this.first) {
@@ -618,6 +696,8 @@ public final class Emitter implements Emitable {
}
states.push(new ExpectBlockSequenceItem(false));
expectNode(false, false, false);
+ inlineCommentsCollector.collectEvents();
+ writeInlineComments();
}
}
}
@@ -642,6 +722,8 @@ public final class Emitter implements Emitable {
}
public void expect() throws IOException {
+ event = blockCommentsCollector.collectEventsAndPoll(event);
+ writeBlockComment();
if (!this.first && event instanceof MappingEndEvent) {
indent = indents.pop();
state = states.pop();
@@ -659,11 +741,37 @@ public final class Emitter implements Emitable {
}
}
+ private boolean isFoldedOrLiteral(Event event) {
+ if(!event.is(ID.Scalar)) {
+ return false;
+ }
+ ScalarEvent scalarEvent = (ScalarEvent) event;
+ ScalarStyle style = scalarEvent.getScalarStyle();
+ return style == ScalarStyle.FOLDED || style == ScalarStyle.LITERAL;
+ }
+
private class ExpectBlockMappingSimpleValue implements EmitterState {
public void expect() throws IOException {
writeIndicator(":", false, false, false);
+ event = inlineCommentsCollector.collectEventsAndPoll(event);
+ if(!isFoldedOrLiteral(event)) {
+ if(writeInlineComments()) {
+ increaseIndent(true, false);
+ writeIndent();
+ indent = indents.pop();
+ }
+ }
+ event = blockCommentsCollector.collectEventsAndPoll(event);
+ if(!blockCommentsCollector.isEmpty()) {
+ increaseIndent(true, false);
+ writeBlockComment();
+ writeIndent();
+ indent = indents.pop();
+ }
states.push(new ExpectBlockMappingKey(false));
expectNode(false, true, false);
+ inlineCommentsCollector.collectEvents();
+ writeInlineComments();
}
}
@@ -671,8 +779,14 @@ public final class Emitter implements Emitable {
public void expect() throws IOException {
writeIndent();
writeIndicator(":", true, false, true);
+ event = inlineCommentsCollector.collectEventsAndPoll(event);
+ writeInlineComments();
+ event = blockCommentsCollector.collectEventsAndPoll(event);
+ writeBlockComment();
states.push(new ExpectBlockMappingKey(false));
expectNode(false, true, false);
+ inlineCommentsCollector.collectEvents(event);
+ writeInlineComments();
}
}
@@ -1316,6 +1430,39 @@ public final class Emitter implements Emitable {
}
writeIndicator("\"", false, false, false);
}
+
+ private boolean writeCommentLines(List<CommentLine> commentLines) throws IOException {
+ int indentColumns = 0;
+ boolean firstComment = true;
+ boolean wroteComment = false;
+ for(CommentLine commentLine : commentLines) {
+ if(commentLine.getCommentType() != CommentType.BLANK_LINE) {
+ if(firstComment) {
+ firstComment = false;
+ writeIndicator("#", commentLine.getCommentType() == CommentType.IN_LINE, false, false);
+ indentColumns = this.column > 0 ? this.column - 1 : 0;
+ } else {
+ writeWhitespace(indentColumns);
+ writeIndicator("#", false, false, false);
+ }
+ stream.write(commentLine.getValue());
+ }
+ writeLineBreak(null);
+ wroteComment = true;
+ }
+ return wroteComment;
+ }
+
+ private void writeBlockComment() throws IOException {
+ if(!blockCommentsCollector.isEmpty()) {
+ writeIndent();
+ }
+ writeCommentLines(blockCommentsCollector.consume());
+ }
+
+ private boolean writeInlineComments() throws IOException {
+ return writeCommentLines(inlineCommentsCollector.consume());
+ }
private String determineBlockHints(String text) {
StringBuilder hints = new StringBuilder();
@@ -1337,7 +1484,9 @@ public final class Emitter implements Emitable {
if (hints.length() > 0 && (hints.charAt(hints.length() - 1) == '+')) {
openEnded = true;
}
- writeLineBreak(null);
+ if(!writeInlineComments()) {
+ writeLineBreak(null);
+ }
boolean leadingSpace = true;
boolean spaces = false;
boolean breaks = true;
@@ -1402,7 +1551,9 @@ public final class Emitter implements Emitable {
if (hints.length() > 0 && (hints.charAt(hints.length() - 1)) == '+') {
openEnded = true;
}
- writeLineBreak(null);
+ if(!writeInlineComments()) {
+ writeLineBreak(null);
+ }
boolean breaks = true;
int start = 0, end = 0;
while (end <= text.length()) {
diff --git a/src/main/java/org/yaml/snakeyaml/events/CommentEvent.java b/src/main/java/org/yaml/snakeyaml/events/CommentEvent.java
index 4ba5e9e7..30236f94 100644
--- a/src/main/java/org/yaml/snakeyaml/events/CommentEvent.java
+++ b/src/main/java/org/yaml/snakeyaml/events/CommentEvent.java
@@ -15,18 +15,13 @@
*/
package org.yaml.snakeyaml.events;
-import org.yaml.snakeyaml.error.Mark;import org.yaml.snakeyaml.tokens.Token;
+import org.yaml.snakeyaml.comments.CommentType;
+import org.yaml.snakeyaml.error.Mark;
/**
- * Marks a scalar value.
+ * Marks a comment block value.
*/
-public final class CommentEvent extends Event {
- public static enum CommentType {
- BLANK_LINE, //
- BLOCK, //
- IN_LINE //
- }
-
+public final class CommentEvent extends Event {
private final CommentType type;
private final String value;
@@ -44,7 +39,7 @@ public final class CommentEvent extends Event {
* Without quotes and escaping.
* </p>
*
- * @return Value as Unicode string.
+ * @return Value a comment line string without the leading '#' or a blank line.
*/
public String getValue() {
return this.value;
@@ -61,7 +56,7 @@ public final class CommentEvent extends Event {
@Override
protected String getArguments() {
- return super.getArguments() + ", value=" + value;
+ return super.getArguments() + "type=" + type + ", value=" + value;
}
@Override
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/Node.java b/src/main/java/org/yaml/snakeyaml/nodes/Node.java
index d3088fc0..fa5030b0 100644
--- a/src/main/java/org/yaml/snakeyaml/nodes/Node.java
+++ b/src/main/java/org/yaml/snakeyaml/nodes/Node.java
@@ -15,19 +15,20 @@
*/
package org.yaml.snakeyaml.nodes;
+import java.util.List;
+
+import org.yaml.snakeyaml.comments.CommentLine;
import org.yaml.snakeyaml.error.Mark;
/**
* Base class for all nodes.
* <p>
- * The nodes form the node-graph described in the <a
- * href="http://yaml.org/spec/1.1/">YAML Specification</a>.
+ * The nodes form the node-graph described in the <a href="http://yaml.org/spec/1.1/">YAML Specification</a>.
* </p>
* <p>
- * While loading, the node graph is usually created by the
- * {@link org.yaml.snakeyaml.composer.Composer}, and later transformed into
- * application specific Java classes by the classes from the
- * {@link org.yaml.snakeyaml.constructor} package.
+ * While loading, the node graph is usually created by the {@link org.yaml.snakeyaml.composer.Composer}, and later
+ * transformed into application specific Java classes by the classes from the {@link org.yaml.snakeyaml.constructor}
+ * package.
* </p>
*/
public abstract class Node {
@@ -37,6 +38,10 @@ public abstract class Node {
private Class<? extends Object> type;
private boolean twoStepsConstruction;
private String anchor;
+ private List<CommentLine> inLineComments;
+ private List<CommentLine> blockComments;
+ // End Comments are only on the last node in a document
+ private List<CommentLine> endComments;
/**
* true when the tag is assigned by the resolver
@@ -52,6 +57,9 @@ public abstract class Node {
this.twoStepsConstruction = false;
this.resolved = true;
this.useClassConstructor = null;
+ this.inLineComments = null;
+ this.blockComments = null;
+ this.endComments = null;
}
/**
@@ -113,13 +121,11 @@ public abstract class Node {
/**
* Indicates if this node must be constructed in two steps.
* <p>
- * Two-step construction is required whenever a node is a child (direct or
- * indirect) of it self. That is, if a recursive structure is build using
- * anchors and aliases.
+ * Two-step construction is required whenever a node is a child (direct or indirect) of it self. That is, if a recursive
+ * structure is build using anchors and aliases.
* </p>
* <p>
- * Set by {@link org.yaml.snakeyaml.composer.Composer}, used during the
- * construction process.
+ * Set by {@link org.yaml.snakeyaml.composer.Composer}, used during the construction process.
* </p>
* <p>
* Only relevant during loading.
@@ -138,8 +144,7 @@ public abstract class Node {
public boolean useClassConstructor() {
if (useClassConstructor == null) {
- if (!tag.isSecondary() && resolved && !Object.class.equals(type)
- && !tag.equals(Tag.NULL)) {
+ if (!tag.isSecondary() && resolved && !Object.class.equals(type) && !tag.equals(Tag.NULL)) {
return true;
} else if (tag.isCompatible(getType())) {
// the tag is compatible with the runtime class
@@ -157,12 +162,12 @@ public abstract class Node {
}
/**
- * Indicates if the tag was added by
- * {@link org.yaml.snakeyaml.resolver.Resolver}.
+ * Indicates if the tag was added by {@link org.yaml.snakeyaml.resolver.Resolver}.
*
* @return true if the tag of this node was resolved
*
- * @deprecated Since v1.22. Absent in immediately prior versions, but present previously. Restored deprecated for backwards compatibility.
+ * @deprecated Since v1.22. Absent in immediately prior versions, but present previously. Restored deprecated for
+ * backwards compatibility.
*/
@Deprecated
public boolean isResolved() {
@@ -176,4 +181,46 @@ public abstract class Node {
public void setAnchor(String anchor) {
this.anchor = anchor;
}
+
+ /**
+ * The ordered list of in-line comments. The first of which appears at the end of the line respresent by this node.
+ * The rest are in the following lines, indented per the Spec to indicate they are continuation of the inline comment.
+ *
+ * @return the comment line list.
+ */
+ public List<CommentLine> getInLineComments() {
+ return inLineComments;
+ }
+
+ public void setInLineComments(List<CommentLine> inLineComments) {
+ this.inLineComments = inLineComments;
+ }
+
+ /**
+ * The ordered list of blank lines and block comments (full line) that appear before this node.
+ *
+ * @return the comment line list.
+ */
+ public List<CommentLine> getBlockComments() {
+ return blockComments;
+ }
+
+ public void setBlockComments(List<CommentLine> blockComments) {
+ this.blockComments = blockComments;
+ }
+
+ /**
+ * The ordered list of blank lines and block comments (full line) that appear AFTER this node.
+ * <p>
+ * NOTE: these comment should occur only in the last node in a document, when walking the node tree "in order"
+ *
+ * @return the comment line list.
+ */
+ public List<CommentLine> getEndComments() {
+ return endComments;
+ }
+
+ public void setEndComments(List<CommentLine> endComments) {
+ this.endComments = endComments;
+ }
}
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/Tag.java b/src/main/java/org/yaml/snakeyaml/nodes/Tag.java
index 3f299000..0c09a018 100644
--- a/src/main/java/org/yaml/snakeyaml/nodes/Tag.java
+++ b/src/main/java/org/yaml/snakeyaml/nodes/Tag.java
@@ -43,6 +43,8 @@ public final class Tag {
public static final Tag STR = new Tag(PREFIX + "str");
public static final Tag SEQ = new Tag(PREFIX + "seq");
public static final Tag MAP = new Tag(PREFIX + "map");
+ // For use to indicate a DUMMY node that contains comments, when there is no other (empty document)
+ public static final Tag COMMENT = new Tag(PREFIX + "comment");
protected static final Map<Tag, Set<Class<?>>> COMPATIBILITY_MAP;
static {
COMPATIBILITY_MAP = new HashMap<Tag, Set<Class<?>>>();
@@ -96,7 +98,8 @@ public final class Tag {
/**
* @deprecated - it will be removed
- * @param uri - URI to be encoded as tag value
+ * @param uri
+ * - URI to be encoded as tag value
*/
public Tag(URI uri) {
if (uri == null) {
@@ -143,13 +146,11 @@ public final class Tag {
}
/**
- * Java has more then 1 class compatible with a language-independent tag
- * (!!int, !!float, !!timestamp etc)
+ * Java has more then 1 class compatible with a language-independent tag (!!int, !!float, !!timestamp etc)
*
* @param clazz
* - Class to check compatibility
- * @return true when the Class can be represented by this
- * language-independent tag
+ * @return true when the Class can be represented by this language-independent tag
*/
public boolean isCompatible(Class<?> clazz) {
Set<Class<?>> set = COMPATIBILITY_MAP.get(this);
diff --git a/src/main/java/org/yaml/snakeyaml/parser/ParserImpl.java b/src/main/java/org/yaml/snakeyaml/parser/ParserImpl.java
index 1b55bb8c..56a9c993 100644
--- a/src/main/java/org/yaml/snakeyaml/parser/ParserImpl.java
+++ b/src/main/java/org/yaml/snakeyaml/parser/ParserImpl.java
@@ -21,11 +21,11 @@ import java.util.Map;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.Version;
+import org.yaml.snakeyaml.comments.CommentType;
import org.yaml.snakeyaml.error.Mark;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.events.AliasEvent;
import org.yaml.snakeyaml.events.CommentEvent;
-import org.yaml.snakeyaml.events.CommentEvent.CommentType;
import org.yaml.snakeyaml.events.DocumentEndEvent;
import org.yaml.snakeyaml.events.DocumentStartEvent;
import org.yaml.snakeyaml.events.Event;
@@ -182,7 +182,7 @@ public class ParserImpl implements Parser {
Mark startMark = token.getStartMark();
Mark endMark = token.getEndMark();
String value = token.getValue();
- CommentType type = CommentType.valueOf(token.getCommentType().name());
+ CommentType type = token.getCommentType();
// state = state, that no change in state
@@ -282,9 +282,6 @@ public class ParserImpl implements Parser {
private class ParseDocumentEnd implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
// Parse the document end.
Token token = scanner.peekToken();
Mark startMark = token.getStartMark();
@@ -395,9 +392,6 @@ public class ParserImpl implements Parser {
private class ParseBlockNode implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
return parseNode(true, false);
}
}
@@ -536,9 +530,6 @@ public class ParserImpl implements Parser {
private class ParseBlockSequenceFirstEntry implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
Token token = scanner.getToken();
marks.push(token.getStartMark());
return new ParseBlockSequenceEntry().produce();
@@ -601,9 +592,6 @@ public class ParserImpl implements Parser {
private class ParseBlockMappingFirstKey implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
Token token = scanner.getToken();
marks.push(token.getStartMark());
return new ParseBlockMappingKey().produce();
@@ -641,13 +629,11 @@ public class ParserImpl implements Parser {
private class ParseBlockMappingValue implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
if (scanner.checkToken(Token.ID.Value)) {
Token token = scanner.getToken();
if (scanner.checkToken(Token.ID.Comment)) {
- return parseBlockNodeOrIndentlessSequence();
+ state = new ParseBlockMappingValueComment();
+ return state.produce();
} else if (!scanner.checkToken(Token.ID.Key, Token.ID.Value, Token.ID.BlockEnd)) {
states.push(new ParseBlockMappingKey());
return parseBlockNodeOrIndentlessSequence();
@@ -665,6 +651,21 @@ public class ParserImpl implements Parser {
}
}
+ private class ParseBlockMappingValueComment implements Production {
+ public Event produce() {
+ if (scanner.checkToken(Token.ID.Comment)) {
+ return parseBlockNodeOrIndentlessSequence();
+ } else if (!scanner.checkToken(Token.ID.Key, Token.ID.Value, Token.ID.BlockEnd)) {
+ states.push(new ParseBlockMappingKey());
+ return parseBlockNodeOrIndentlessSequence();
+ } else {
+ state = new ParseBlockMappingKey();
+ Token token = scanner.getToken();
+ return processEmptyScalar(token.getEndMark());
+ }
+ }
+ }
+
/**
* <pre>
* flow_sequence ::= FLOW-SEQUENCE-START
@@ -680,9 +681,6 @@ public class ParserImpl implements Parser {
*/
private class ParseFlowSequenceFirstEntry implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
Token token = scanner.getToken();
marks.push(token.getStartMark());
return new ParseFlowSequenceEntry(true).produce();
@@ -697,9 +695,6 @@ public class ParserImpl implements Parser {
}
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
if (!scanner.checkToken(Token.ID.FlowSequenceEnd)) {
if (!first) {
if (scanner.checkToken(Token.ID.FlowEntry)) {
@@ -724,17 +719,27 @@ public class ParserImpl implements Parser {
}
Token token = scanner.getToken();
Event event = new SequenceEndEvent(token.getStartMark(), token.getEndMark());
- state = states.pop();
marks.pop();
+ if(!scanner.checkToken(Token.ID.Comment)) {
+ state = states.pop();
+ } else {
+ state = new ParseFlowEndComment();
+ }
return event;
}
}
- private class ParseFlowSequenceEntryMappingKey implements Production {
+ private class ParseFlowEndComment implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
+ Event event = produceCommentEvent((CommentToken)scanner.getToken());
+ if(!scanner.checkToken(Token.ID.Comment)) {
+ state = states.pop();
}
+ return event;
+ }
+ }
+ private class ParseFlowSequenceEntryMappingKey implements Production {
+ public Event produce() {
Token token = scanner.getToken();
if (!scanner.checkToken(Token.ID.Value, Token.ID.FlowEntry, Token.ID.FlowSequenceEnd)) {
states.push(new ParseFlowSequenceEntryMappingValue());
@@ -748,9 +753,6 @@ public class ParserImpl implements Parser {
private class ParseFlowSequenceEntryMappingValue implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
if (scanner.checkToken(Token.ID.Value)) {
Token token = scanner.getToken();
if (!scanner.checkToken(Token.ID.FlowEntry, Token.ID.FlowSequenceEnd)) {
@@ -770,9 +772,6 @@ public class ParserImpl implements Parser {
private class ParseFlowSequenceEntryMappingEnd implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
state = new ParseFlowSequenceEntry(false);
Token token = scanner.peekToken();
return new MappingEndEvent(token.getStartMark(), token.getEndMark());
@@ -790,9 +789,6 @@ public class ParserImpl implements Parser {
*/
private class ParseFlowMappingFirstKey implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
Token token = scanner.getToken();
marks.push(token.getStartMark());
return new ParseFlowMappingKey(true).produce();
@@ -835,17 +831,18 @@ public class ParserImpl implements Parser {
}
Token token = scanner.getToken();
Event event = new MappingEndEvent(token.getStartMark(), token.getEndMark());
- state = states.pop();
marks.pop();
- return event;
+ if(!scanner.checkToken(Token.ID.Comment)) {
+ state = states.pop();
+ } else {
+ state = new ParseFlowEndComment();
+ }
+ return event;
}
}
private class ParseFlowMappingValue implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
if (scanner.checkToken(Token.ID.Value)) {
Token token = scanner.getToken();
if (!scanner.checkToken(Token.ID.FlowEntry, Token.ID.FlowMappingEnd)) {
@@ -865,9 +862,6 @@ public class ParserImpl implements Parser {
private class ParseFlowMappingEmptyValue implements Production {
public Event produce() {
- if (scanner.checkToken(Token.ID.Comment)) {
- return produceCommentEvent((CommentToken) scanner.getToken());
- }
state = new ParseFlowMappingKey(false);
return processEmptyScalar(scanner.peekToken().getStartMark());
}
diff --git a/src/main/java/org/yaml/snakeyaml/scanner/ScannerImpl.java b/src/main/java/org/yaml/snakeyaml/scanner/ScannerImpl.java
index db48297d..8eb43920 100644
--- a/src/main/java/org/yaml/snakeyaml/scanner/ScannerImpl.java
+++ b/src/main/java/org/yaml/snakeyaml/scanner/ScannerImpl.java
@@ -26,6 +26,7 @@ import java.util.Map;
import java.util.regex.Pattern;
import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.comments.CommentType;
import org.yaml.snakeyaml.error.Mark;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.reader.StreamReader;
@@ -36,7 +37,6 @@ import org.yaml.snakeyaml.tokens.BlockEntryToken;
import org.yaml.snakeyaml.tokens.BlockMappingStartToken;
import org.yaml.snakeyaml.tokens.BlockSequenceStartToken;
import org.yaml.snakeyaml.tokens.CommentToken;
-import org.yaml.snakeyaml.tokens.CommentToken.CommentType;
import org.yaml.snakeyaml.tokens.DirectiveToken;
import org.yaml.snakeyaml.tokens.DocumentEndToken;
import org.yaml.snakeyaml.tokens.DocumentStartToken;
@@ -112,7 +112,8 @@ public final class ScannerImpl implements Scanner {
* &#92;UHHHHHHHH : escaped 32-bit Unicode character
* </pre>
*
- * @see <a href="http://yaml.org/spec/1.1/current.html#id872840">5.6. Escape Sequences</a>
+ * @see <a href="http://yaml.org/spec/1.1/current.html#id872840">5.6. Escape
+ * Sequences</a>
*/
public final static Map<Character, Integer> ESCAPE_CODES = new HashMap<Character, Integer>();
@@ -303,12 +304,8 @@ public final class ScannerImpl implements Scanner {
* Fetch one or more tokens from the StreamReader.
*/
private void fetchMoreTokens() {
- // Eat whitespaces until we reach the next token.
- int startColumn = reader.getColumn();
- List<Token> blankLines = scanToNextToken();
- startColumn = reader.getColumn() == 0 ? 0 : startColumn;
- // Process blank lines as comments
- fetchBlankLine(blankLines);
+ // Eat whitespaces and process comments until we reach the next token.
+ scanToNextToken();
// Remove obsolete possible simple keys.
stalePossibleSimpleKeys();
// Compare the current indentation and column. It may add some tokens
@@ -322,9 +319,6 @@ public final class ScannerImpl implements Scanner {
// Is it the end of stream?
fetchStreamEnd();
return;
- case '#':
- fetchComment(startColumn == 0 ? CommentType.BLOCK : CommentType.IN_LINE);
- return;
case '%':
// Is it a directive?
if (checkDirective()) {
@@ -625,24 +619,6 @@ public final class ScannerImpl implements Scanner {
this.done = true;
}
- private void fetchBlankLine(List<Token> tokens) {
- // See the specification for details.
- if (emitComments && tokens != null) {
- this.tokens.addAll(tokens);
- }
- }
-
- /**
- * Fetch a comment (both block and in-line)
- * @param type TODO
- */
- private void fetchComment(CommentType type) {
- Token tok = scanComment(type);
- if (emitComments) {
- this.tokens.add(tok);
- }
- }
-
/**
* Fetch a YAML directive. Directives are presentation details that are
* interpreted as instructions to the processor. YAML defines two kinds of
@@ -1086,8 +1062,8 @@ public final class ScannerImpl implements Scanner {
this.allowSimpleKey = false;
// Scan and add SCALAR. May change `allow_simple_key`.
- List<Token> tok = scanPlain();
- this.tokens.addAll(tok);
+ Token tok = scanPlain();
+ this.tokens.add(tok);
}
// Checkers.
@@ -1216,18 +1192,17 @@ public final class ScannerImpl implements Scanner {
* Scanners for block, flow, and plain scalars need to be modified.
* </pre>
*/
- private List<Token> scanToNextToken() {
- List<Token> blankLineTokenList = null;
- if(reader.getLine() == 0 && reader.getColumn() == 0) {
- blankLineTokenList = new ArrayList<Token>();
- }
+ private void scanToNextToken() {
// If there is a byte order mark (BOM) at the beginning of the stream,
// forward past it.
if (reader.getIndex() == 0 && reader.peek() == 0xFEFF) {
reader.forward();
}
boolean found = false;
+ int inlineStartColumn = -1;
while (!found) {
+ Mark startMark = reader.getMark();
+ boolean commentSeen = false;
int ff = 0;
// Peek ahead until we find the first non-space character, then
// move forward directly to that character.
@@ -1237,17 +1212,35 @@ public final class ScannerImpl implements Scanner {
if (ff > 0) {
reader.forward(ff);
}
+ // If the character we have skipped forward to is a comment (#),
+ // then peek ahead until we find the next end of line. YAML
+ // comments are from a # to the next new-line. We then forward
+ // past the comment.
+ if (reader.peek() == '#') {
+ commentSeen = true;
+ CommentType type;
+ if(startMark.getColumn() != 0) {
+ type = CommentType.IN_LINE;
+ inlineStartColumn = reader.getColumn();
+ } else if(inlineStartColumn == reader.getColumn()) {
+ type = CommentType.IN_LINE;
+ } else {
+ inlineStartColumn = -1;
+ type = CommentType.BLOCK;
+ }
+ CommentToken token = scanComment(type);
+ if (emitComments) {
+ this.tokens.add(token);
+ }
+ }
// If we scanned a line break, then (depending on flow level),
// simple keys may be allowed.
- Mark startMark = reader.getMark();
String breaks = scanLineBreak();
if (breaks.length() != 0) {// found a line-break
- if(emitComments) {
- if (blankLineTokenList == null) {
- blankLineTokenList = new ArrayList<Token>();
- } else {
+ if (emitComments && ! commentSeen) {
+ if (startMark.getColumn() == 0) {
Mark endMark = reader.getMark();
- blankLineTokenList.add(new CommentToken(CommentType.BLANK_LINE, breaks, startMark, endMark));
+ this.tokens.add(new CommentToken(CommentType.BLANK_LINE, breaks, startMark, endMark));
}
}
if (this.flowLevel == 0) {
@@ -1259,7 +1252,6 @@ public final class ScannerImpl implements Scanner {
found = true;
}
}
- return blankLineTokenList;
}
private CommentToken scanComment(CommentType type) {
@@ -1270,8 +1262,8 @@ public final class ScannerImpl implements Scanner {
while (Constant.NULL_OR_LINEBR.hasNo(reader.peek(length))) {
length++;
}
- Mark endMark = reader.getMark();
String value = reader.prefixForward(length);
+ Mark endMark = reader.getMark();
return new CommentToken(type, value, startMark, endMark);
}
@@ -1699,7 +1691,7 @@ public final class ScannerImpl implements Scanner {
}
CommentToken blankLineCommentToken = null;
if (chompi.chompTailIsTrue()) {
- if(emitComments) {
+ if (emitComments) {
blankLineCommentToken = new CommentToken(CommentType.BLANK_LINE, breaks, startMark, endMark);
}
chunks.append(breaks);
@@ -2044,15 +2036,12 @@ public final class ScannerImpl implements Scanner {
* Indentation rules are loosed for the flow context.
* </pre>
*/
- private List<Token> scanPlain() {
+ private Token scanPlain() {
StringBuilder chunks = new StringBuilder();
Mark startMark = reader.getMark();
Mark endMark = startMark;
- Mark spacesStartMark = null;
- Mark spacesEndMark = null;
int indent = this.indent + 1;
String spaces = "";
- CommentToken commentToken = null;
while (true) {
int c;
int length = 0;
@@ -2076,24 +2065,14 @@ public final class ScannerImpl implements Scanner {
chunks.append(spaces);
chunks.append(reader.prefixForward(length));
endMark = reader.getMark();
- spacesStartMark = endMark;
spaces = scanPlainSpaces();
- spacesEndMark = reader.getMark();
// System.out.printf("spaces[%s]\n", spaces);
if (spaces.length() == 0 || reader.peek() == '#'
|| (this.flowLevel == 0 && this.reader.getColumn() < indent)) {
break;
}
}
- if (reader.peek() == '#') {
- commentToken = scanComment(CommentType.IN_LINE);
- }
- ScalarToken scalarToken = new ScalarToken(chunks.toString(), startMark, endMark, true);
- CommentToken blankLineCommentToken = null;
- if (spaces.trim().length() > 0) {
- blankLineCommentToken = new CommentToken(CommentType.BLANK_LINE, spaces, spacesStartMark, spacesEndMark);
- }
- return makeTokenList(scalarToken, commentToken, blankLineCommentToken);
+ return new ScalarToken(chunks.toString(), startMark, endMark, true);
}
/**
@@ -2331,15 +2310,14 @@ public final class ScannerImpl implements Scanner {
}
return "";
}
-
-
+
private List<Token> makeTokenList(Token... tokens) {
List<Token> tokenList = new ArrayList<>();
- for(int ix = 0; ix < tokens.length; ix++) {
- if(tokens[ix] == null) {
+ for (int ix = 0; ix < tokens.length; ix++) {
+ if (tokens[ix] == null) {
continue;
}
- if(!emitComments && (tokens[ix] instanceof CommentToken)) {
+ if (!emitComments && (tokens[ix] instanceof CommentToken)) {
continue;
}
tokenList.add(tokens[ix]);
diff --git a/src/main/java/org/yaml/snakeyaml/serializer/Serializer.java b/src/main/java/org/yaml/snakeyaml/serializer/Serializer.java
index fd638b12..ed51e83c 100644
--- a/src/main/java/org/yaml/snakeyaml/serializer/Serializer.java
+++ b/src/main/java/org/yaml/snakeyaml/serializer/Serializer.java
@@ -24,8 +24,10 @@ import java.util.Set;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.Version;
+import org.yaml.snakeyaml.comments.CommentLine;
import org.yaml.snakeyaml.emitter.Emitable;
import org.yaml.snakeyaml.events.AliasEvent;
+import org.yaml.snakeyaml.events.CommentEvent;
import org.yaml.snakeyaml.events.DocumentEndEvent;
import org.yaml.snakeyaml.events.DocumentStartEvent;
import org.yaml.snakeyaml.events.ImplicitTuple;
@@ -37,7 +39,6 @@ import org.yaml.snakeyaml.events.SequenceStartEvent;
import org.yaml.snakeyaml.events.StreamEndEvent;
import org.yaml.snakeyaml.events.StreamStartEvent;
import org.yaml.snakeyaml.nodes.AnchorNode;
-import org.yaml.snakeyaml.nodes.CollectionNode;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeId;
@@ -151,7 +152,7 @@ public final class Serializer {
}
}
- //parent Node is not used but might be used in the future
+ // parent Node is not used but might be used in the future
private void serializeNode(Node node, Node parent) throws IOException {
if (node.getNodeId() == NodeId.anchor) {
node = ((AnchorNode) node).getRealNode();
@@ -164,6 +165,7 @@ public final class Serializer {
switch (node.getNodeId()) {
case scalar:
ScalarNode scalarNode = (ScalarNode) node;
+ serializeComments(node.getBlockComments());
Tag detectedTag = this.resolver.resolve(NodeId.scalar, scalarNode.getValue(), true);
Tag defaultTag = this.resolver.resolve(NodeId.scalar, scalarNode.getValue(), false);
ImplicitTuple tuple = new ImplicitTuple(node.getTag().equals(detectedTag), node
@@ -171,9 +173,12 @@ public final class Serializer {
ScalarEvent event = new ScalarEvent(tAlias, node.getTag().getValue(), tuple,
scalarNode.getValue(), null, null, scalarNode.getScalarStyle());
this.emitter.emit(event);
+ serializeComments(node.getInLineComments());
+ serializeComments(node.getEndComments());
break;
case sequence:
SequenceNode seqNode = (SequenceNode) node;
+ serializeComments(node.getBlockComments());
boolean implicitS = node.getTag().equals(this.resolver.resolve(NodeId.sequence,
null, true));
this.emitter.emit(new SequenceStartEvent(tAlias, node.getTag().getValue(),
@@ -183,22 +188,40 @@ public final class Serializer {
serializeNode(item, node);
}
this.emitter.emit(new SequenceEndEvent(null, null));
+ serializeComments(node.getInLineComments());
+ serializeComments(node.getEndComments());
break;
default:// instance of MappingNode
+ serializeComments(node.getBlockComments());
Tag implicitTag = this.resolver.resolve(NodeId.mapping, null, true);
boolean implicitM = node.getTag().equals(implicitTag);
- this.emitter.emit(new MappingStartEvent(tAlias, node.getTag().getValue(),
- implicitM, null, null, ((CollectionNode) node).getFlowStyle()));
MappingNode mnode = (MappingNode) node;
List<NodeTuple> map = mnode.getValue();
- for (NodeTuple row : map) {
- Node key = row.getKeyNode();
- Node value = row.getValueNode();
- serializeNode(key, mnode);
- serializeNode(value, mnode);
+ if (mnode.getTag() != Tag.COMMENT ) {
+ this.emitter.emit(new MappingStartEvent(tAlias, mnode.getTag().getValue(), implicitM, null, null,
+ mnode.getFlowStyle()));
+ for (NodeTuple row : map) {
+ Node key = row.getKeyNode();
+ Node value = row.getValueNode();
+ serializeNode(key, mnode);
+ serializeNode(value, mnode);
+ }
+ this.emitter.emit(new MappingEndEvent(null, null));
+ serializeComments(node.getInLineComments());
+ serializeComments(node.getEndComments());
}
- this.emitter.emit(new MappingEndEvent(null, null));
}
}
}
+
+ private void serializeComments(List<CommentLine> comments) throws IOException {
+ if(comments == null) {
+ return;
+ }
+ for (CommentLine line : comments) {
+ CommentEvent commentEvent = new CommentEvent(line.getCommentType(), line.getValue(), line.getStartMark(),
+ line.getEndMark());
+ this.emitter.emit(commentEvent);
+ }
+ }
}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/CommentToken.java b/src/main/java/org/yaml/snakeyaml/tokens/CommentToken.java
index 2e98a730..9dea88fc 100644
--- a/src/main/java/org/yaml/snakeyaml/tokens/CommentToken.java
+++ b/src/main/java/org/yaml/snakeyaml/tokens/CommentToken.java
@@ -17,15 +17,10 @@ package org.yaml.snakeyaml.tokens;
import java.util.Objects;
+import org.yaml.snakeyaml.comments.CommentType;
import org.yaml.snakeyaml.error.Mark;
public final class CommentToken extends Token {
- public static enum CommentType {
- BLANK_LINE, //
- BLOCK, //
- IN_LINE; //
- }
-
private final CommentType type;
private final String value;
diff --git a/src/test/java/org/yaml/snakeyaml/comment/ComposerWithCommentEnabledTest.java b/src/test/java/org/yaml/snakeyaml/comment/ComposerWithCommentEnabledTest.java
new file mode 100644
index 00000000..4f592fb3
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/comment/ComposerWithCommentEnabledTest.java
@@ -0,0 +1,528 @@
+package org.yaml.snakeyaml.comment;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import org.junit.Test;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.comments.CommentLine;
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.Event.ID;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.parser.Parser;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+public class ComposerWithCommentEnabledTest {
+
+ @SuppressWarnings("unused")
+ private void assertEventListEquals(List<ID> expectedEventIdList, Parser parser) {
+ for (ID expectedEventId : expectedEventIdList) {
+ parser.checkEvent(expectedEventId);
+ Event event = parser.getEvent();
+ System.out.println(event);
+ if (event == null) {
+ fail("Missing event: " + expectedEventId);
+ }
+ assertEquals(expectedEventId, event.getEventId());
+ }
+ }
+
+ private void printBlockComment(Node node, int level, PrintStream out) {
+ if (node.getBlockComments() != null) {
+ List<CommentLine> blockComments = node.getBlockComments();
+ for (int i = 0; i < blockComments.size(); i++) {
+ printWithIndent("Block Comment", level, out);
+ }
+ }
+ }
+
+ private void printEndComment(Node node, int level, PrintStream out) {
+ if (node.getEndComments() != null) {
+ List<CommentLine> endComments = node.getEndComments();
+ for (int i = 0; i < endComments.size(); i++) {
+ printWithIndent("End Comment", level, out);
+ }
+ }
+ }
+
+ private void printInLineComment(Node node, int level, PrintStream out) {
+ if (node.getInLineComments() != null) {
+ List<CommentLine> inLineComments = node.getInLineComments();
+ for (int i = 0; i < inLineComments.size(); i++) {
+ printWithIndent("InLine Comment", level + 1, out);
+ }
+ }
+ }
+
+ private void printWithIndent(String line, int level, PrintStream out) {
+ for (int ix = 0; ix < level; ix++) {
+ out.print(" ");
+ }
+ out.println(line);
+ }
+
+ private void printNodeInternal(Node node, int level, PrintStream out) {
+
+ if (node instanceof MappingNode) {
+ MappingNode mappingNode = (MappingNode) node;
+ printBlockComment(mappingNode, level, out);
+ printWithIndent(mappingNode.getClass().getSimpleName(), level, out);
+ for (NodeTuple childNodeTuple : mappingNode.getValue()) {
+ printWithIndent("Tuple", level + 1, out);
+ printNodeInternal(childNodeTuple.getKeyNode(), level + 2, out);
+ printNodeInternal(childNodeTuple.getValueNode(), level + 2, out);
+ }
+ printInLineComment(mappingNode, level, out);
+ printEndComment(mappingNode, level, out);
+
+ } else if (node instanceof SequenceNode) {
+ SequenceNode sequenceNode = (SequenceNode) node;
+ printBlockComment(sequenceNode, level, out);
+ printWithIndent(sequenceNode.getClass().getSimpleName(), level, out);
+ for (Node childNode : sequenceNode.getValue()) {
+ printNodeInternal(childNode, level + 1, out);
+ }
+ printInLineComment(sequenceNode, level, out);
+ printEndComment(sequenceNode, level, out);
+
+ } else if (node instanceof ScalarNode) {
+ ScalarNode scalarNode = (ScalarNode) node;
+ printBlockComment(scalarNode, level, out);
+ printWithIndent(scalarNode.getClass().getSimpleName() + ": " + scalarNode.getValue(), level, out);
+ printInLineComment(scalarNode, level, out);
+ printEndComment(scalarNode, level, out);
+
+ } else {
+ printBlockComment(node, level, out);
+ printWithIndent(node.getClass().getSimpleName(), level, out);
+ printInLineComment(node, level, out);
+ printEndComment(node, level, out);
+ }
+ }
+
+ private void printNodeList(List<Node> nodeList) {
+ System.out.println("BEGIN");
+ for (Node node : nodeList) {
+ printNodeInternal(node, 1, System.out);
+ }
+ System.out.println("DONE\n");
+ }
+
+ private List<Node> getNodeList(Composer composer) {
+ List<Node> nodeList = new ArrayList<>();
+ while (composer.checkNode()) {
+ nodeList.add(composer.getNode());
+ }
+ return nodeList;
+ }
+
+ private void assertNodesEqual(String[] expecteds, List<Node> nodeList) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (PrintStream out = new PrintStream(baos)) {
+ for (Node node : nodeList) {
+ printNodeInternal(node, 0, out);
+ }
+ }
+ String actualString = baos.toString();
+ String[] actuals = actualString.split("\n");
+ for(int ix = 0; ix < Math.min(expecteds.length, actuals.length); ix++) {
+ assertEquals(expecteds[ix], actuals[ix]);
+ }
+ assertEquals(expecteds.length, actuals.length);
+ }
+
+ public Composer newComposerWithCommentsEnabled(String data) {
+ return new Composer(new ParserImpl(new StreamReader(data), true), new Resolver());
+ }
+
+ @Test
+ public void testEmpty() {
+ String data = "";
+ String[] expecteds = new String[] { //
+ "" //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void testParseWithOnlyComment() {
+ String data = "# Comment";
+ String[] expecteds = new String[] { //
+ "Block Comment", //
+ "MappingNode", //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void testCommentEndingALine() {
+ String data = "" + //
+ "key: # Comment\n" + //
+ " value\n";
+
+ String[] expecteds = new String[] { //
+ "MappingNode", //
+ " Tuple", //
+ " ScalarNode: key", //
+ " InLine Comment", //
+ " ScalarNode: value" //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void testMultiLineComment() {
+ String data = "" + //
+ "key: # Comment\n" + //
+ " # lines\n" + //
+ " value\n" + //
+ "\n";
+
+ String[] expecteds = new String[] { //
+ "MappingNode", //
+ " Tuple", //
+ " ScalarNode: key", //
+ " InLine Comment", //
+ " InLine Comment", //
+ " ScalarNode: value" //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void testBlankLine() {
+ String data = "" + //
+ "\n";
+
+ String[] expecteds = new String[] { //
+ "Block Comment", //
+ "MappingNode", //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void testBlankLineComments() {
+ String data = "" + //
+ "\n" + //
+ "abc: def # commment\n" + //
+ "\n" + //
+ "\n";
+
+ String[] expecteds = new String[] { //
+ "Block Comment", //
+ "MappingNode", //
+ " Tuple", //
+ " ScalarNode: abc", //
+ " ScalarNode: def", //
+ " InLine Comment", //
+ "End Comment", //
+ "End Comment", //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void test_blockScalar() {
+ String data = "" + //
+ "abc: > # Comment\n" + //
+ " def\n" + //
+ " hij\n" + //
+ "\n";
+
+ String[] expecteds = new String[] { //
+ "MappingNode", //
+ " Tuple", //
+ " ScalarNode: abc", //
+ " InLine Comment", //
+ " ScalarNode: def hij" //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void testDirectiveLineEndComment() {
+ String data = "%YAML 1.1 #Comment\n";
+
+ String[] expecteds = new String[] { //
+ "" //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void testSequence() {
+ String data = "" + //
+ "# Comment\n" + //
+ "list: # InlineComment1\n" + //
+ "# Block Comment\n" + //
+ "- item # InlineComment2\n" + //
+ "# Comment\n";
+
+ String[] expecteds = new String[] { //
+ "Block Comment", //
+ "MappingNode", //
+ " Tuple", //
+ " ScalarNode: list", //
+ " InLine Comment", //
+ " Block Comment", //
+ " SequenceNode", //
+ " ScalarNode: item", //
+ " InLine Comment", //
+ "End Comment" //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void testAllComments1() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "# Block Comment2\n" + //
+ "key: # Inline Comment1a\n" + //
+ " # Inline Comment1b\n" + //
+ " # Block Comment3a\n" + //
+ " # Block Comment3b\n" + //
+ " value # Inline Comment2\n" + //
+ "# Block Comment4\n" + //
+ "list: # InlineComment3a\n" + //
+ " # InlineComment3b\n" + //
+ "# Block Comment5\n" + //
+ "- item1 # InlineComment4\n" + //
+ "- item2: [ value2a, value2b ] # InlineComment5\n" + //
+ "- item3: { key3a: [ value3a1, value3a2 ], key3b: value3b } # InlineComment6\n" + //
+ "# Block Comment6\n" + //
+ "---\n" + //
+ "# Block Comment7\n" + //
+ "";
+
+ String[] expecteds = new String[] { //
+ "Block Comment", //
+ "Block Comment", //
+ "MappingNode", //
+ " Tuple", //
+ " ScalarNode: key", //
+ " InLine Comment", //
+ " InLine Comment", //
+ " Block Comment", //
+ " Block Comment", //
+ " ScalarNode: value", //
+ " InLine Comment", //
+ " Tuple", //
+ " Block Comment", //
+ " ScalarNode: list", //
+ " InLine Comment", //
+ " InLine Comment", //
+ " Block Comment", //
+ " SequenceNode", //
+ " ScalarNode: item1", //
+ " InLine Comment", //
+ " MappingNode", //
+ " Tuple", //
+ " ScalarNode: item2", //
+ " SequenceNode", //
+ " ScalarNode: value2a", //
+ " ScalarNode: value2b", //
+ " InLine Comment", //
+ " MappingNode", //
+ " Tuple", //
+ " ScalarNode: item3", //
+ " MappingNode", //
+ " Tuple", //
+ " ScalarNode: key3a", //
+ " SequenceNode", //
+ " ScalarNode: value3a1", //
+ " ScalarNode: value3a2", //
+ " Tuple", //
+ " ScalarNode: key3b", //
+ " ScalarNode: value3b", //
+ " InLine Comment", //
+ "End Comment", //
+ "Block Comment", //
+ "ScalarNode: ", // FIXME: should not be here
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void testAllComments2() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "# Block Comment2\n" + //
+ "- item1 # Inline Comment1a\n" + //
+ " # Inline Comment1b\n" + //
+ "# Block Comment3a\n" + //
+ "# Block Comment3b\n" + //
+ "- item2: value # Inline Comment2\n" + //
+ "# Block Comment4\n" + //
+ "";
+
+ String[] expecteds = new String[] { //
+ "Block Comment", //
+ "Block Comment", //
+ "SequenceNode", //
+ " ScalarNode: item1", //
+ " InLine Comment", //
+ " InLine Comment", //
+ " Block Comment", //
+ " Block Comment", //
+ " MappingNode", //
+ " Tuple", //
+ " ScalarNode: item2", //
+ " ScalarNode: value", //
+ " InLine Comment", //
+ "End Comment", //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+ Node newNode = new Yaml().compose(new StringReader("a: b"));
+ result.add(newNode);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void testAllComments3() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "[ item1, item2: value2, {item3: value3} ] # Inline Comment1\n" + //
+ "# Block Comment2\n" + //
+ "";
+
+ String[] expecteds = new String[] { //
+ "Block Comment", //
+ "SequenceNode", //
+ " ScalarNode: item1", //
+ " MappingNode", //
+ " Tuple", //
+ " ScalarNode: item2", //
+ " ScalarNode: value2", //
+ " MappingNode", //
+ " Tuple", //
+ " ScalarNode: item3", //
+ " ScalarNode: value3", //
+ " InLine Comment", //
+ "End Comment", //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = getNodeList(sut);
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ @Test
+ public void testGetSingleNode() {
+ String data = "" + //
+ "\n" + //
+ "abc: def # commment\n" + //
+ "\n" + //
+ "\n";
+ String[] expecteds = new String[] { //
+ "MappingNode", //
+ " Tuple", //
+ " ScalarNode: abc", //
+ " ScalarNode: def", //
+ " InLine Comment", //
+ "End Comment", //
+ "End Comment", //
+ };
+
+ Composer sut = newComposerWithCommentsEnabled(data);
+ List<Node> result = Arrays.asList(new Node[] { sut.getSingleNode() });
+
+ printNodeList(result);
+ assertNodesEqual(expecteds, result);
+ }
+
+ private static class TestConstructor extends SafeConstructor {
+ }
+
+ @Test
+ public void testBaseConstructorGetData() {
+ String data = "" + //
+ "\n" + //
+ "abc: def # commment\n" + //
+ "\n" + //
+ "\n";
+
+ TestConstructor sut = new TestConstructor();
+ sut.setComposer(newComposerWithCommentsEnabled(data));
+ Object result = sut.getData();
+ assertTrue(result instanceof LinkedHashMap);
+ @SuppressWarnings("unchecked")
+ LinkedHashMap<String, Object> map = (LinkedHashMap<String, Object>) result;
+ assertEquals(1, map.size());
+ assertEquals(map.get("abc"), "def");
+ }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/comment/EmitterWithCommentEnabledTest.java b/src/test/java/org/yaml/snakeyaml/comment/EmitterWithCommentEnabledTest.java
new file mode 100644
index 00000000..4bb8a174
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/comment/EmitterWithCommentEnabledTest.java
@@ -0,0 +1,213 @@
+package org.yaml.snakeyaml.comment;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.junit.Test;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.emitter.Emitter;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.resolver.Resolver;
+import org.yaml.snakeyaml.serializer.Serializer;
+
+public class EmitterWithCommentEnabledTest {
+
+ private String runEmitterWithCommentsEnabled(String data) throws IOException {
+ StringWriter output = new StringWriter();
+
+ Tag rootTag = null;
+ DumperOptions options = new DumperOptions();
+ options.setDefaultScalarStyle(ScalarStyle.PLAIN);
+ options.setDefaultFlowStyle(FlowStyle.BLOCK);
+ Serializer serializer = new Serializer(new Emitter(output, options), new Resolver(), options, rootTag);
+
+ serializer.open();
+ Composer composer = new Composer(new ParserImpl(new StreamReader(data), true), new Resolver());
+ while (composer.checkNode()) {
+ serializer.serialize(composer.getNode());
+ }
+ serializer.close();
+
+ return output.toString();
+ }
+
+ @Test
+ public void testEmpty() throws Exception {
+ String data = "";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+
+ @Test
+ public void testWithOnlyComment() throws Exception {
+ String data = "# Comment\n\n";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+
+ @Test
+ public void testCommentEndingALine() throws Exception {
+ String data = "" + //
+ "key: # Comment\n" + //
+ " value\n";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+
+ @Test
+ public void testMultiLineComment() throws Exception {
+ String data = "" + //
+ "key: # Comment\n" + //
+ " # lines\n" + //
+ " value\n";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+
+ @Test
+ public void testBlankLine() throws Exception {
+ String data = "" + //
+ "\n";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+
+ @Test
+ public void testBlankLineComments() throws Exception {
+ String data = "" + //
+ "\n" + //
+ "abc: def # commment\n" + //
+ "\n" + //
+ "\n";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+
+ @Test
+ public void testBlockScalar() throws Exception {
+ String data = "" + //
+ "abc: | # Comment\n" + //
+ " def\n" + //
+ " hij\n";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+
+ @Test
+ public void testDirectiveLineEndComment() throws Exception {
+ String data = "%YAML 1.1 #Comment\n";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ // We currently strip Directive comments
+ assertEquals("", result);
+ }
+
+ @Test
+ public void testSequence() throws Exception {
+ String data = "" + //
+ "# Comment\n" + //
+ "list: # InlineComment1\n" + //
+ " # Block Comment\n" + //
+ " - item # InlineComment2\n" + //
+ "# Comment\n";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+
+ @Test
+ public void testAllComments1() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "# Block Comment2\n" + //
+ "key: # Inline Comment1a\n" + //
+ " # Inline Comment1b\n" + //
+ " # Block Comment3a\n" + //
+ " # Block Comment3b\n" + //
+ " value # Inline Comment2\n" + //
+ "# Block Comment4\n" + //
+ "list: # InlineComment3a\n" + //
+ " # InlineComment3b\n" + //
+ " # Block Comment5\n" + //
+ " - item1 # InlineComment4\n" + //
+ " - item2: [value2a, value2b] # InlineComment5\n" + //
+ " - item3: {key3a: [value3a1, value3a2], key3b: value3b} # InlineComment6\n" + //
+ "# Block Comment6\n" + //
+ "---\n" + //
+ "# Block Comment7\n" + //
+ "";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+
+ @Test
+ public void testMultiDoc() throws Exception {
+ String data = "" + //
+ "key: value\n" + //
+ "# Block Comment\n" + //
+ "---\n" + //
+ "# Block Comment\n" + //
+ "key: value\n" + //
+ "";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+
+ @Test
+ public void testAllComments2() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "# Block Comment2\n" + //
+ "- item1 # Inline Comment1a\n" + //
+ " # Inline Comment1b\n" + //
+ "# Block Comment3a\n" + //
+ "# Block Comment3b\n" + //
+ "- item2: value # Inline Comment2\n" + //
+ "# Block Comment4\n" + //
+ "";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+
+ @Test
+ public void testAllComments3() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "[item1, {item2: value2}, {item3: value3}] # Inline Comment1\n" + //
+ "# Block Comment2\n" + //
+ "";
+
+ String result = runEmitterWithCommentsEnabled(data);
+
+ assertEquals(data, result);
+ }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/comment/ParserWithCommentEnabledTest.java b/src/test/java/org/yaml/snakeyaml/comment/ParserWithCommentEnabledTest.java
index 7613e2de..5a31e082 100644
--- a/src/test/java/org/yaml/snakeyaml/comment/ParserWithCommentEnabledTest.java
+++ b/src/test/java/org/yaml/snakeyaml/comment/ParserWithCommentEnabledTest.java
@@ -19,7 +19,9 @@ public class ParserWithCommentEnabledTest {
for (ID expectedEventId : expectedEventIdList) {
parser.checkEvent(expectedEventId);
Event event = parser.getEvent();
- System.out.println(event);
+ System.out.println("Expected: " + expectedEventId);
+ System.out.println("Got: " + event);
+ System.out.println();
if (event == null) {
fail("Missing event: " + expectedEventId);
}
@@ -30,7 +32,8 @@ public class ParserWithCommentEnabledTest {
@SuppressWarnings("unused")
private void printEventList(Parser parser) {
for (Event event = parser.getEvent(); event != null; event = parser.getEvent()) {
- System.out.println(event);
+ System.out.println("Got: " + event);
+ System.out.println();
}
}
@@ -174,4 +177,177 @@ public class ParserWithCommentEnabledTest {
Parser sut = new ParserImpl(new StreamReader(data), true);
assertEventListEquals(expectedEventIdList, sut);
}
+
+ @Test
+ public void testSequence() {
+ String data = "" + //
+ "# Comment\n" + //
+ "list: # InlineComment1\n" + //
+ "# Block Comment\n" + //
+ "- item # InlineComment2\n" + //
+ "# Comment\n";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.Comment, //
+ ID.DocumentStart, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Comment,
+ ID.Comment, //
+ ID.SequenceStart, //
+ ID.Scalar, ID.Comment, //
+ ID.Comment, //
+ ID.SequenceEnd, //
+ ID.MappingEnd, //
+ ID.DocumentEnd, //
+ ID.StreamEnd //
+ });
+
+ Parser sut = new ParserImpl(new StreamReader(data), true);
+
+ assertEventListEquals(expectedEventIdList, sut);
+ }
+
+ @Test
+ public void testAllComments1() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "# Block Comment2\n" + //
+ "key: # Inline Comment1a\n" + //
+ " # Inline Comment1b\n" + //
+ " # Block Comment3a\n" + //
+ " # Block Comment3b\n" + //
+ " value # Inline Comment2\n" + //
+ "# Block Comment4\n" + //
+ "list: # InlineComment3a\n" + //
+ " # InlineComment3b\n" + //
+ "# Block Comment5\n" + //
+ "- item1 # InlineComment4\n" + //
+ "- item2: [ value2a, value2b ] # InlineComment5\n" + //
+ "- item3: { key3a: [ value3a1, value3a2 ], key3b: value3b } # InlineComment6\n" + //
+ "# Block Comment6\n" + //
+ "---\n" + //
+ "# Block Comment7\n" + //
+ "";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.Comment, //
+ ID.Comment, //
+ ID.DocumentStart, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Comment, ID.Comment, //
+
+ ID.Comment, ID.Comment, //
+ ID.Scalar, ID.Comment, //
+
+ ID.Comment, //
+ ID.Scalar, ID.Comment, ID.Comment, //
+ ID.Comment, //
+
+ ID.SequenceStart, //
+ ID.Scalar, ID.Comment, //
+ ID.MappingStart, //
+ ID.Scalar, ID.SequenceStart, ID.Scalar, ID.Scalar, ID.SequenceEnd, ID.Comment, //
+ ID.MappingEnd,
+
+ ID.MappingStart, //
+ ID.Scalar, // value=item3
+ ID.MappingStart, //
+ ID.Scalar, // value=key3a
+ ID.SequenceStart, //
+ ID.Scalar, // value=value3a
+ ID.Scalar, //value=value3a2
+ ID.SequenceEnd, //
+ ID.Scalar, // value=key3b
+ ID.Scalar, // value=value3b
+ ID.MappingEnd, //
+ ID.Comment, // type=IN_LINE, value= InlineComment6
+ ID.Comment, //
+ ID.MappingEnd, //
+ ID.SequenceEnd, //
+ ID.MappingEnd,
+ ID.DocumentEnd, //
+
+ ID.DocumentStart, //
+ ID.Comment, //
+ ID.Scalar, // Empty
+ ID.DocumentEnd, //
+ ID.StreamEnd //
+ });
+
+ Parser sut = new ParserImpl(new StreamReader(data), true);
+
+ //printEventList(sut);
+ assertEventListEquals(expectedEventIdList, sut);
+ }
+
+ @Test
+ public void testAllComments2() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "# Block Comment2\n" + //
+ "- item1 # Inline Comment1a\n" + //
+ " # Inline Comment1b\n" + //
+ "# Block Comment3a\n" + //
+ "# Block Comment3b\n" + //
+ "- item2: value # Inline Comment2\n" + //
+ "# Block Comment4\n" + //
+ "";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.Comment, //
+ ID.Comment, //
+ ID.DocumentStart, //
+ ID.SequenceStart, //
+ ID.Scalar, ID.Comment, ID.Comment, //
+ ID.Comment, //
+ ID.Comment, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Scalar, ID.Comment, //
+ ID.Comment, //
+ ID.MappingEnd, //
+ ID.SequenceEnd, //
+ ID.DocumentEnd, //
+ ID.StreamEnd //
+ });
+
+ Parser sut = new ParserImpl(new StreamReader(data), true);
+
+ assertEventListEquals(expectedEventIdList, sut);
+ }
+
+ @Test
+ public void testAllComments3() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "[ item1, item2: value2, {item3: value3} ] # Inline Comment1\n" + //
+ "# Block Comment2\n" + //
+ "";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.Comment, //
+ ID.DocumentStart, //
+ ID.SequenceStart, //
+ ID.Scalar,
+ ID.MappingStart, //
+ ID.Scalar, ID.Scalar, //
+ ID.MappingEnd, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Scalar, //
+ ID.MappingEnd, //
+ ID.SequenceEnd, //
+ ID.Comment, //
+ ID.Comment, //
+ ID.DocumentEnd, //
+ ID.StreamEnd //
+ });
+
+ Parser sut = new ParserImpl(new StreamReader(data), true);
+
+// printEventList(sut);
+ assertEventListEquals(expectedEventIdList, sut);
+ }
}
diff --git a/src/test/java/org/yaml/snakeyaml/comment/SerializerWithCommentEnabledTest.java b/src/test/java/org/yaml/snakeyaml/comment/SerializerWithCommentEnabledTest.java
new file mode 100644
index 00000000..1ed7ccb9
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/comment/SerializerWithCommentEnabledTest.java
@@ -0,0 +1,389 @@
+package org.yaml.snakeyaml.comment;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.emitter.Emitable;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.Event.ID;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.resolver.Resolver;
+import org.yaml.snakeyaml.serializer.Serializer;
+
+public class SerializerWithCommentEnabledTest {
+
+ private void assertEventListEquals(List<ID> expectedEventIdList, List<Event> actualEvents) {
+ Iterator<Event> iterator = actualEvents.iterator();
+ for (ID expectedEventId : expectedEventIdList) {
+ System.out.println("Expected: " + expectedEventId);
+ assertTrue(iterator.hasNext());
+ Event event = iterator.next();
+ System.out.println("Got: " + event);
+ System.out.println();
+ assertEquals(expectedEventId, event.getEventId());
+ }
+ }
+
+ private static class TestEmitter implements Emitable {
+ private List<Event> eventList = new ArrayList<>();
+
+ @Override
+ public void emit(Event event) throws IOException {
+ eventList.add(event);
+ }
+
+ public List<Event> getEventList() {
+ return eventList;
+ }
+ }
+
+ public List<Event> serializeWithCommentsEnabled(String data) throws IOException {
+ TestEmitter emitter = new TestEmitter();
+ Tag rootTag = null;
+ Serializer serializer = new Serializer(emitter, new Resolver(), new DumperOptions(), rootTag);
+ serializer.open();
+ Composer composer = new Composer(new ParserImpl(new StreamReader(data), true), new Resolver());
+ while (composer.checkNode()) {
+ serializer.serialize(composer.getNode());
+ }
+ serializer.close();
+ List<Event> events = emitter.getEventList();
+ System.out.println("RESULT: ");
+ for(Event event: events) {
+ System.out.println(event);
+ }
+ System.out.println();
+ return events;
+ }
+
+
+ @Test
+ public void testEmpty() throws Exception {
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { ID.StreamStart, ID.StreamEnd });
+
+ String data = "";
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+
+ @Test
+ public void testParseWithOnlyComment() throws Exception {
+ String data = "# Comment";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.DocumentStart, //
+ ID.Comment, //
+ ID.DocumentEnd, //
+ ID.StreamEnd, //
+ });
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+
+ @Test
+ public void testCommentEndingALine() throws Exception {
+ String data = "" + //
+ "key: # Comment\n" + //
+ " value\n";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.DocumentStart, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Comment, ID.Scalar, //
+ ID.MappingEnd, //
+ ID.DocumentEnd, //
+ ID.StreamEnd });
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+
+ @Test
+ public void testMultiLineComment() throws Exception {
+ String data = "" + //
+ "key: # Comment\n" + //
+ " # lines\n" + //
+ " value\n" + //
+ "\n";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { ID.StreamStart, //
+ ID.DocumentStart, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Comment, ID.Comment, ID.Scalar, //
+ ID.MappingEnd, //
+ ID.DocumentEnd, //
+ ID.StreamEnd });
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+
+ @Test
+ public void testBlankLine() throws Exception {
+ String data = "" + //
+ "\n";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.DocumentStart, //
+ ID.Comment, //
+ ID.DocumentEnd, //
+ ID.StreamEnd });
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+
+ @Test
+ public void testBlankLineComments() throws Exception {
+ String data = "" + //
+ "\n" + //
+ "abc: def # commment\n" + //
+ "\n" + //
+ "\n";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.DocumentStart, //
+ ID.Comment, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Scalar, ID.Comment, //
+ ID.MappingEnd, //
+ ID.Comment, //
+ ID.Comment, //
+ ID.DocumentEnd, //
+ ID.StreamEnd });
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+
+ @Test
+ public void test_blockScalar() throws Exception {
+ String data = "" + //
+ "abc: > # Comment\n" + //
+ " def\n" + //
+ " hij\n" + //
+ "\n";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.DocumentStart, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Comment, //
+ ID.Scalar, //
+ ID.MappingEnd, //
+ ID.DocumentEnd, //
+ ID.StreamEnd //
+ });
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+
+ @Test
+ public void testDirectiveLineEndComment() throws Exception {
+ String data = "%YAML 1.1 #Comment\n";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.StreamEnd //
+ });
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+
+ @Test
+ public void testSequence() throws Exception {
+ String data = "" + //
+ "# Comment\n" + //
+ "list: # InlineComment1\n" + //
+ "# Block Comment\n" + //
+ "- item # InlineComment2\n" + //
+ "# Comment\n";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.DocumentStart, //
+ ID.Comment, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Comment, ID.Comment, //
+ ID.SequenceStart, //
+ ID.Scalar, ID.Comment, //
+ ID.SequenceEnd, //
+ ID.MappingEnd, //
+ ID.Comment, //
+ ID.DocumentEnd, //
+ ID.StreamEnd //
+ });
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+
+ @Test
+ public void testAllComments1() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "# Block Comment2\n" + //
+ "key: # Inline Comment1a\n" + //
+ " # Inline Comment1b\n" + //
+ " # Block Comment3a\n" + //
+ " # Block Comment3b\n" + //
+ " value # Inline Comment2\n" + //
+ "# Block Comment4\n" + //
+ "list: # InlineComment3a\n" + //
+ " # InlineComment3b\n" + //
+ "# Block Comment5\n" + //
+ "- item1 # InlineComment4\n" + //
+ "- item2: [ value2a, value2b ] # InlineComment5\n" + //
+ "- item3: { key3a: [ value3a1, value3a2 ], key3b: value3b } # InlineComment6\n" + //
+ "# Block Comment6\n" + //
+ "---\n" + //
+ "# Block Comment7\n" + //
+ "";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.DocumentStart, //
+ ID.Comment, //
+ ID.Comment, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Comment, ID.Comment, //
+
+ ID.Comment, ID.Comment, //
+ ID.Scalar, ID.Comment, //
+
+ ID.Comment, //
+ ID.Scalar, ID.Comment, ID.Comment, //
+ ID.Comment, //
+
+ ID.SequenceStart, //
+ ID.Scalar, ID.Comment, //
+ ID.MappingStart, //
+ ID.Scalar, ID.SequenceStart, ID.Scalar, ID.Scalar, ID.SequenceEnd, ID.Comment, //
+ ID.MappingEnd,
+
+ ID.MappingStart, //
+ ID.Scalar, // value=item3
+ ID.MappingStart, //
+ ID.Scalar, // value=key3a
+ ID.SequenceStart, //
+ ID.Scalar, // value=value3a
+ ID.Scalar, // value=value3a2
+ ID.SequenceEnd, //
+ ID.Scalar, // value=key3b
+ ID.Scalar, // value=value3b
+ ID.MappingEnd, //
+ ID.Comment, // type=IN_LINE, value= InlineComment6
+ ID.MappingEnd, //
+ ID.SequenceEnd, //
+ ID.MappingEnd, //
+ ID.Comment, //
+ ID.DocumentEnd, //
+
+ ID.DocumentStart, //
+ ID.Comment, //
+ ID.Scalar, // Empty
+ ID.DocumentEnd, //
+ ID.StreamEnd //
+ });
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+
+ @Test
+ public void testAllComments2() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "# Block Comment2\n" + //
+ "- item1 # Inline Comment1a\n" + //
+ " # Inline Comment1b\n" + //
+ "# Block Comment3a\n" + //
+ "# Block Comment3b\n" + //
+ "- item2: value # Inline Comment2\n" + //
+ "# Block Comment4\n" + //
+ "";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.DocumentStart, //
+ ID.Comment, //
+ ID.Comment, //
+ ID.SequenceStart, //
+ ID.Scalar, ID.Comment, ID.Comment, //
+ ID.Comment, //
+ ID.Comment, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Scalar, ID.Comment, //
+ ID.MappingEnd, //
+ ID.SequenceEnd, //
+ ID.Comment, //
+ ID.DocumentEnd, //
+ ID.StreamEnd //
+ });
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+
+ @Test
+ public void testAllComments3() throws Exception {
+ String data = "" + //
+ "# Block Comment1\n" + //
+ "[ item1, item2: value2, {item3: value3} ] # Inline Comment1\n" + //
+ "# Block Comment2\n" + //
+ "";
+
+ List<ID> expectedEventIdList = Arrays.asList(new ID[] { //
+ ID.StreamStart, //
+ ID.DocumentStart, //
+ ID.Comment, //
+ ID.SequenceStart, //
+ ID.Scalar, ID.MappingStart, //
+ ID.Scalar, ID.Scalar, //
+ ID.MappingEnd, //
+ ID.MappingStart, //
+ ID.Scalar, ID.Scalar, //
+ ID.MappingEnd, //
+ ID.SequenceEnd, //
+ ID.Comment, //
+ ID.Comment, //
+ ID.DocumentEnd, //
+ ID.StreamEnd //
+ });
+
+ List<Event> result = serializeWithCommentsEnabled(data);
+
+ assertEventListEquals(expectedEventIdList, result);
+ }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java b/src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java
index 11a13598..c810137e 100644
--- a/src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java
+++ b/src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java
@@ -130,6 +130,10 @@ public class EmitterTest extends TestCase {
emitter.emit(new DocumentStartEvent(null, null, false, null, null));
emitter.emit(new ScalarEvent(null, null, new ImplicitTuple(true, false), burger
+ halfBurger, null, null, DumperOptions.ScalarStyle.DOUBLE_QUOTED));
+ // Needed as emitter won't process above event until it peeks at this one
+ // to be sure it is not a comment
+ emitter.emit(new ScalarEvent(null, null, new ImplicitTuple(true, false), "", null,
+ null, DumperOptions.ScalarStyle.PLAIN));
String expected = "! \"\\U0001f354\\ud83c\"";
assertEquals(expected, output.toString());
}
diff --git a/src/test/java/org/yaml/snakeyaml/emitter/EmptyStringOutputTest.java b/src/test/java/org/yaml/snakeyaml/emitter/EmptyStringOutputTest.java
index 3df77520..4540e199 100644
--- a/src/test/java/org/yaml/snakeyaml/emitter/EmptyStringOutputTest.java
+++ b/src/test/java/org/yaml/snakeyaml/emitter/EmptyStringOutputTest.java
@@ -45,6 +45,9 @@ public class EmptyStringOutputTest extends TestCase {
emitter.emit(new StreamStartEvent(null, null));
emitter.emit(new DocumentStartEvent(null, null, false, null, null));
emitter.emit(new ScalarEvent(null, null, new ImplicitTuple(true, false), value, null, null, DumperOptions.ScalarStyle.PLAIN));
+ // Needed as emitter won't process above event until it peeks at this one
+ // to be sure it is not a comment
+ emitter.emit(new ScalarEvent(null, null, new ImplicitTuple(true, false), value, null, null, DumperOptions.ScalarStyle.PLAIN));
return output.toString();
}
}