1 /**
2  * Copyright (c) 2008, SnakeYAML
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 package org.yaml.snakeyaml.comment;
15 
16 import static org.junit.Assert.assertEquals;
17 import static org.junit.Assert.assertTrue;
18 
19 import java.io.ByteArrayOutputStream;
20 import java.io.PrintStream;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.LinkedHashMap;
24 import java.util.List;
25 import org.junit.Test;
26 import org.yaml.snakeyaml.comments.CommentLine;
27 import org.yaml.snakeyaml.composer.Composer;
28 import org.yaml.snakeyaml.constructor.SafeConstructor;
29 import org.yaml.snakeyaml.nodes.MappingNode;
30 import org.yaml.snakeyaml.nodes.Node;
31 import org.yaml.snakeyaml.nodes.NodeTuple;
32 import org.yaml.snakeyaml.nodes.ScalarNode;
33 import org.yaml.snakeyaml.nodes.SequenceNode;
34 import org.yaml.snakeyaml.parser.ParserImpl;
35 import org.yaml.snakeyaml.reader.StreamReader;
36 import org.yaml.snakeyaml.resolver.Resolver;
37 
38 public class ComposerWithCommentEnabledTest {
39 
40   private final boolean DEBUG = false;
41 
printBlockComment(Node node, int level, PrintStream out)42   private void printBlockComment(Node node, int level, PrintStream out) {
43     if (node.getBlockComments() != null) {
44       List<CommentLine> blockComments = node.getBlockComments();
45       for (int i = 0; i < blockComments.size(); i++) {
46         printWithIndent("Block Comment", level, out);
47       }
48     }
49   }
50 
printEndComment(Node node, int level, PrintStream out)51   private void printEndComment(Node node, int level, PrintStream out) {
52     if (node.getEndComments() != null) {
53       List<CommentLine> endComments = node.getEndComments();
54       for (int i = 0; i < endComments.size(); i++) {
55         printWithIndent("End Comment", level, out);
56       }
57     }
58   }
59 
printInLineComment(Node node, int level, PrintStream out)60   private void printInLineComment(Node node, int level, PrintStream out) {
61     if (node.getInLineComments() != null) {
62       List<CommentLine> inLineComments = node.getInLineComments();
63       for (int i = 0; i < inLineComments.size(); i++) {
64         printWithIndent("InLine Comment", level + 1, out);
65       }
66     }
67   }
68 
printWithIndent(String line, int level, PrintStream out)69   private void printWithIndent(String line, int level, PrintStream out) {
70     for (int ix = 0; ix < level; ix++) {
71       out.print("    ");
72     }
73     out.print(line);
74     out.print("\n");
75   }
76 
printNodeInternal(Node node, int level, PrintStream out)77   private void printNodeInternal(Node node, int level, PrintStream out) {
78 
79     if (node instanceof MappingNode) {
80       MappingNode mappingNode = (MappingNode) node;
81       printBlockComment(mappingNode, level, out);
82       printWithIndent(mappingNode.getClass().getSimpleName(), level, out);
83       for (NodeTuple childNodeTuple : mappingNode.getValue()) {
84         printWithIndent("Tuple", level + 1, out);
85         printNodeInternal(childNodeTuple.getKeyNode(), level + 2, out);
86         printNodeInternal(childNodeTuple.getValueNode(), level + 2, out);
87       }
88       printInLineComment(mappingNode, level, out);
89       printEndComment(mappingNode, level, out);
90 
91     } else if (node instanceof SequenceNode) {
92       SequenceNode sequenceNode = (SequenceNode) node;
93       printBlockComment(sequenceNode, level, out);
94       printWithIndent(sequenceNode.getClass().getSimpleName(), level, out);
95       for (Node childNode : sequenceNode.getValue()) {
96         printNodeInternal(childNode, level + 1, out);
97       }
98       printInLineComment(sequenceNode, level, out);
99       printEndComment(sequenceNode, level, out);
100 
101     } else if (node instanceof ScalarNode) {
102       ScalarNode scalarNode = (ScalarNode) node;
103       printBlockComment(scalarNode, level, out);
104       printWithIndent(scalarNode.getClass().getSimpleName() + ": " + scalarNode.getValue(), level,
105           out);
106       printInLineComment(scalarNode, level, out);
107       printEndComment(scalarNode, level, out);
108 
109     } else {
110       printBlockComment(node, level, out);
111       printWithIndent(node.getClass().getSimpleName(), level, out);
112       printInLineComment(node, level, out);
113       printEndComment(node, level, out);
114     }
115   }
116 
printNodeList(List<Node> nodeList)117   private void printNodeList(List<Node> nodeList) {
118     if (DEBUG) {
119       System.out.println("BEGIN");
120       boolean first = true;
121       for (Node node : nodeList) {
122         if (first) {
123           first = false;
124         } else {
125           System.out.println("---");
126         }
127         printNodeInternal(node, 1, System.out);
128       }
129       System.out.println("DONE\n");
130     }
131   }
132 
getNodeList(Composer composer)133   private List<Node> getNodeList(Composer composer) {
134     List<Node> nodeList = new ArrayList<>();
135     while (composer.checkNode()) {
136       nodeList.add(composer.getNode());
137     }
138     return nodeList;
139   }
140 
assertNodesEqual(String[] expected, List<Node> nodeList)141   private void assertNodesEqual(String[] expected, List<Node> nodeList) {
142     ByteArrayOutputStream baos = new ByteArrayOutputStream();
143     boolean first = true;
144     try (PrintStream out = new PrintStream(baos)) {
145       for (Node node : nodeList) {
146         if (first) {
147           first = false;
148         } else {
149           out.print("---\n");
150         }
151         printNodeInternal(node, 0, out);
152       }
153     }
154     String actualString = baos.toString();
155     String[] actuals = actualString.split("\n");
156     for (int ix = 0; ix < Math.min(expected.length, actuals.length); ix++) {
157       assertEquals(expected[ix], actuals[ix]);
158     }
159     assertEquals(expected.length, actuals.length);
160   }
161 
newComposerWithCommentsEnabled(String data)162   public Composer newComposerWithCommentsEnabled(String data) {
163     return new Composer(new ParserImpl(new StreamReader(data), true), new Resolver());
164   }
165 
166   @Test
testEmpty()167   public void testEmpty() {
168     String data = "";
169     String[] expected = new String[] { //
170         "" //
171     };
172 
173     Composer sut = newComposerWithCommentsEnabled(data);
174     List<Node> result = getNodeList(sut);
175 
176     printNodeList(result);
177     assertNodesEqual(expected, result);
178   }
179 
180   @Test
testParseWithOnlyComment()181   public void testParseWithOnlyComment() {
182     String data = "# Comment";
183     String[] expected = new String[] { //
184         "Block Comment", //
185         "MappingNode", //
186     };
187 
188     Composer sut = newComposerWithCommentsEnabled(data);
189     List<Node> result = getNodeList(sut);
190 
191     printNodeList(result);
192     assertNodesEqual(expected, result);
193   }
194 
195   @Test
testCommentEndingALine()196   public void testCommentEndingALine() {
197     String data = "" + //
198         "key: # Comment\n" + //
199         "  value\n";
200 
201     String[] expected = new String[] { //
202         "MappingNode", //
203         "    Tuple", //
204         "        ScalarNode: key", //
205         "            InLine Comment", //
206         "        ScalarNode: value" //
207     };
208 
209     Composer sut = newComposerWithCommentsEnabled(data);
210     List<Node> result = getNodeList(sut);
211 
212     printNodeList(result);
213     assertNodesEqual(expected, result);
214   }
215 
216   @Test
testMultiLineComment()217   public void testMultiLineComment() {
218     String data = "" + //
219         "key: # Comment\n" + //
220         "     # lines\n" + //
221         "  value\n" + //
222         "\n";
223 
224     String[] expected = new String[] { //
225         "MappingNode", //
226         "    Tuple", //
227         "        ScalarNode: key", //
228         "            InLine Comment", //
229         "            InLine Comment", //
230         "        ScalarNode: value", //
231         "End Comment" //
232     };
233 
234     Composer sut = newComposerWithCommentsEnabled(data);
235     List<Node> result = getNodeList(sut);
236 
237     printNodeList(result);
238     assertNodesEqual(expected, result);
239   }
240 
241   @Test
testBlankLine()242   public void testBlankLine() {
243     String data = "" + //
244         "\n";
245 
246     String[] expected = new String[] { //
247         "Block Comment", //
248         "MappingNode", //
249     };
250 
251     Composer sut = newComposerWithCommentsEnabled(data);
252     List<Node> result = getNodeList(sut);
253 
254     printNodeList(result);
255     assertNodesEqual(expected, result);
256   }
257 
258   @Test
testBlankLineComments()259   public void testBlankLineComments() {
260     String data = "" + //
261         "\n" + //
262         "abc: def # commment\n" + //
263         "\n" + //
264         "\n";
265 
266     String[] expected = new String[] { //
267         "MappingNode", //
268         "    Tuple", //
269         "        Block Comment", //
270         "        ScalarNode: abc", //
271         "        ScalarNode: def", //
272         "            InLine Comment", //
273         "End Comment", //
274         "End Comment", //
275     };
276 
277     Composer sut = newComposerWithCommentsEnabled(data);
278     List<Node> result = getNodeList(sut);
279 
280     printNodeList(result);
281     assertNodesEqual(expected, result);
282   }
283 
284   @Test
test_blockScalar()285   public void test_blockScalar() {
286     String data = "" + //
287         "abc: > # Comment\n" + //
288         "    def\n" + //
289         "    hij\n" + //
290         "\n";
291 
292     String[] expected = new String[] { //
293         "MappingNode", //
294         "    Tuple", //
295         "        ScalarNode: abc", //
296         "            InLine Comment", //
297         "        ScalarNode: def hij" //
298     };
299 
300     Composer sut = newComposerWithCommentsEnabled(data);
301     List<Node> result = getNodeList(sut);
302 
303     printNodeList(result);
304     assertNodesEqual(expected, result);
305   }
306 
307   @Test
testDirectiveLineEndComment()308   public void testDirectiveLineEndComment() {
309     String data = "%YAML 1.1 #Comment\n";
310 
311     String[] expected = new String[] { //
312         "" //
313     };
314 
315     Composer sut = newComposerWithCommentsEnabled(data);
316     List<Node> result = getNodeList(sut);
317 
318     printNodeList(result);
319     assertNodesEqual(expected, result);
320   }
321 
322   @Test
testSequence()323   public void testSequence() {
324     String data = "" + //
325         "# Comment\n" + //
326         "list: # InlineComment1\n" + //
327         "# Block Comment\n" + //
328         "- item # InlineComment2\n" + //
329         "# Comment\n";
330 
331     String[] expected = new String[] { //
332         "MappingNode", //
333         "    Tuple", //
334         "        Block Comment", //
335         "        ScalarNode: list", //
336         "            InLine Comment", //
337         "        SequenceNode", //
338         "            Block Comment", //
339         "            ScalarNode: item", //
340         "                InLine Comment", //
341         "End Comment" //
342     };
343 
344     Composer sut = newComposerWithCommentsEnabled(data);
345     List<Node> result = getNodeList(sut);
346 
347     printNodeList(result);
348     assertNodesEqual(expected, result);
349   }
350 
351   @Test
testAllComments1()352   public void testAllComments1() throws Exception {
353     String data = "" + //
354         "# Block Comment1\n" + //
355         "# Block Comment2\n" + //
356         "key: # Inline Comment1a\n" + //
357         "     # Inline Comment1b\n" + //
358         "  # Block Comment3a\n" + //
359         "  # Block Comment3b\n" + //
360         "  value # Inline Comment2\n" + //
361         "# Block Comment4\n" + //
362         "list: # InlineComment3a\n" + //
363         "      # InlineComment3b\n" + //
364         "# Block Comment5\n" + //
365         "- item1 # InlineComment4\n" + //
366         "- item2: [ value2a, value2b ] # InlineComment5\n" + //
367         "- item3: { key3a: [ value3a1, value3a2 ], key3b: value3b } # InlineComment6\n" + //
368         "# Block Comment6\n" + //
369         "---\n" + //
370         "# Block Comment7\n" + //
371         "";
372 
373     String[] expected = new String[] { //
374         "MappingNode", //
375         "    Tuple", //
376         "        Block Comment", //
377         "        Block Comment", //
378         "        ScalarNode: key", //
379         "            InLine Comment", //
380         "            InLine Comment", //
381         "        Block Comment", //
382         "        Block Comment", //
383         "        ScalarNode: value", //
384         "            InLine Comment", //
385         "    Tuple", //
386         "        Block Comment", //
387         "        ScalarNode: list", //
388         "            InLine Comment", //
389         "            InLine Comment", //
390         "        SequenceNode", //
391         "            Block Comment", //
392         "            ScalarNode: item1", //
393         "                InLine Comment", //
394         "            MappingNode", //
395         "                Tuple", //
396         "                    ScalarNode: item2", //
397         "                    SequenceNode", //
398         "                        ScalarNode: value2a", //
399         "                        ScalarNode: value2b", //
400         "                        InLine Comment", //
401         "            MappingNode", //
402         "                Tuple", //
403         "                    ScalarNode: item3", //
404         "                    MappingNode", //
405         "                        Tuple", //
406         "                            ScalarNode: key3a", //
407         "                            SequenceNode", //
408         "                                ScalarNode: value3a1", //
409         "                                ScalarNode: value3a2", //
410         "                        Tuple", //
411         "                            ScalarNode: key3b", //
412         "                            ScalarNode: value3b", //
413         "                        InLine Comment", //
414         "End Comment", //
415         "---", //
416         "Block Comment", //
417         "ScalarNode: ", // This is an empty scalar created as this is an empty document
418     };
419 
420     Composer sut = newComposerWithCommentsEnabled(data);
421     List<Node> result = getNodeList(sut);
422 
423     printNodeList(result);
424     assertNodesEqual(expected, result);
425   }
426 
427   @Test
testAllComments2()428   public void testAllComments2() throws Exception {
429     String data = "" + //
430         "# Block Comment1\n" + //
431         "# Block Comment2\n" + //
432         "- item1 # Inline Comment1a\n" + //
433         "        # Inline Comment1b\n" + //
434         "# Block Comment3a\n" + //
435         "# Block Comment3b\n" + //
436         "- item2: value # Inline Comment2\n" + //
437         "# Block Comment4\n" + //
438         "";
439 
440     String[] expected = new String[] { //
441         "SequenceNode", //
442         "    Block Comment", //
443         "    Block Comment", //
444         "    ScalarNode: item1", //
445         "        InLine Comment", //
446         "        InLine Comment", //
447         "    MappingNode", //
448         "        Tuple", //
449         "            Block Comment", //
450         "            Block Comment", //
451         "            ScalarNode: item2", //
452         "            ScalarNode: value", //
453         "                InLine Comment", //
454         "End Comment", //
455     };
456 
457     Composer sut = newComposerWithCommentsEnabled(data);
458     List<Node> result = getNodeList(sut);
459 
460     printNodeList(result);
461     assertNodesEqual(expected, result);
462   }
463 
464   @Test
testAllComments3()465   public void testAllComments3() throws Exception {
466     String data = "" + //
467         "# Block Comment1\n" + //
468         "[ item1, item2: value2, {item3: value3} ] # Inline Comment1\n" + //
469         "# Block Comment2\n" + //
470         "";
471 
472     String[] expected = new String[] { //
473         "Block Comment", //
474         "SequenceNode", //
475         "    ScalarNode: item1", //
476         "    MappingNode", //
477         "        Tuple", //
478         "            ScalarNode: item2", //
479         "            ScalarNode: value2", //
480         "    MappingNode", //
481         "        Tuple", //
482         "            ScalarNode: item3", //
483         "            ScalarNode: value3", //
484         "    InLine Comment", //
485         "End Comment", //
486     };
487 
488     Composer sut = newComposerWithCommentsEnabled(data);
489     List<Node> result = getNodeList(sut);
490 
491     printNodeList(result);
492     assertNodesEqual(expected, result);
493   }
494 
495   @Test
testGetSingleNode()496   public void testGetSingleNode() {
497     String data = "" + //
498         "\n" + //
499         "abc: def # commment\n" + //
500         "\n" + //
501         "\n";
502     String[] expected = new String[] { //
503         "MappingNode", //
504         "    Tuple", //
505         "        Block Comment", "        ScalarNode: abc", //
506         "        ScalarNode: def", //
507         "            InLine Comment", //
508         "End Comment", //
509         "End Comment", //
510     };
511 
512     Composer sut = newComposerWithCommentsEnabled(data);
513     List<Node> result = Collections.singletonList(sut.getSingleNode());
514 
515     printNodeList(result);
516     assertNodesEqual(expected, result);
517   }
518 
519   @Test
testGetSingleNodeHeaderComment()520   public void testGetSingleNodeHeaderComment() {
521     String data = "" + //
522         "\n" + //
523         "# Block Comment1\n" + //
524         "# Block Comment2\n" + //
525         "abc: def # commment\n" + //
526         "\n" + //
527         "\n";
528     String[] expected = new String[] { //
529         "MappingNode", //
530         "    Tuple", //
531         "        Block Comment", //
532         "        Block Comment", //
533         "        Block Comment", //
534         "        ScalarNode: abc", //
535         "        ScalarNode: def", //
536         "            InLine Comment", //
537         "End Comment", //
538         "End Comment", //
539     };
540 
541     Composer sut = newComposerWithCommentsEnabled(data);
542     List<Node> result = Collections.singletonList(sut.getSingleNode());
543 
544     printNodeList(result);
545     assertNodesEqual(expected, result);
546   }
547 
548   private static class TestConstructor extends SafeConstructor {
549 
550   }
551 
552   @Test
testBaseConstructorGetData()553   public void testBaseConstructorGetData() {
554     String data = "" + //
555         "\n" + //
556         "abc: def # commment\n" + //
557         "\n" + //
558         "\n";
559 
560     TestConstructor sut = new TestConstructor();
561     sut.setComposer(newComposerWithCommentsEnabled(data));
562     Object result = sut.getData();
563     assertTrue(result instanceof LinkedHashMap);
564     @SuppressWarnings("unchecked")
565     LinkedHashMap<String, Object> map = (LinkedHashMap<String, Object>) result;
566     assertEquals(1, map.size());
567     assertEquals(map.get("abc"), "def");
568   }
569 
570   @Test
testEmptyEntryInMap()571   public void testEmptyEntryInMap() {
572     String data = "userProps:\n" + //
573         "#password\n" + //
574         "pass: mySecret\n";
575     String[] expected = new String[] { //
576         "MappingNode", //
577         "    Tuple", //
578         "        ScalarNode: userProps", //
579         "        ScalarNode: ", //
580         "    Tuple", //
581         "        Block Comment", //
582         "        ScalarNode: pass", //
583         "        ScalarNode: mySecret", //
584     };
585 
586     Composer sut = newComposerWithCommentsEnabled(data);
587     List<Node> result = Collections.singletonList(sut.getSingleNode());
588 
589     printNodeList(result);
590     assertNodesEqual(expected, result);
591   }
592 }
593