##### Coverage Report - org.yaml.snakeyaml.composer.Composer

Classes in this File | Line Coverage | Branch Coverage | Complexity
Composer |
99% | 101/102
94% | 47/50
| 4.222222222222222;4.222

1 |  |
     /**
2 |  |
      * Copyright (c) 2008-2011, http://www.snakeyaml.org
3 |  |
      *
4 |  |
      * Licensed under the Apache License, Version 2.0 (the "License");
5 |  |
      * you may not use this file except in compliance with the License.
6 |  |
      * You may obtain a copy of the License at
7 |  |
      *
8 |  |
      *     http://www.apache.org/licenses/LICENSE-2.0
9 |  |
      *
10 |  |
      * Unless required by applicable law or agreed to in writing, software
11 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
12 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 |  |
      * See the License for the specific language governing permissions and
14 |  |
      * limitations under the License.
15 |  |
      */
16 |  |
17 |  |
     package org.yaml.snakeyaml.composer;
18 |  |
19 |  |
     import java.util.ArrayList;
20 |  |
     import java.util.HashMap;
21 |  |
     import java.util.HashSet;
22 |  |
     import java.util.List;
23 |  |
     import java.util.Map;
24 |  |
     import java.util.Set;
25 |  |
26 |  |
     import org.yaml.snakeyaml.events.AliasEvent;
27 |  |
     import org.yaml.snakeyaml.events.Event;
28 |  |
     import org.yaml.snakeyaml.events.MappingStartEvent;
29 |  |
     import org.yaml.snakeyaml.events.NodeEvent;
30 |  |
     import org.yaml.snakeyaml.events.ScalarEvent;
31 |  |
     import org.yaml.snakeyaml.events.SequenceStartEvent;
32 |  |
     import org.yaml.snakeyaml.nodes.MappingNode;
33 |  |
     import org.yaml.snakeyaml.nodes.Node;
34 |  |
     import org.yaml.snakeyaml.nodes.NodeId;
35 |  |
     import org.yaml.snakeyaml.nodes.NodeTuple;
36 |  |
     import org.yaml.snakeyaml.nodes.ScalarNode;
37 |  |
     import org.yaml.snakeyaml.nodes.SequenceNode;
38 |  |
     import org.yaml.snakeyaml.nodes.Tag;
39 |  |
     import org.yaml.snakeyaml.parser.Parser;
40 |  |
     import org.yaml.snakeyaml.resolver.Resolver;
41 |  |
42 |  |
     /**
43 |  |
      * Creates a node graph from parser events.
44 |  |
      * <p>
45 |  |
      * Corresponds to the 'Compose' step as described in chapter 3.1 of the <a
46 |  |
      * href="http://yaml.org/spec/1.1/">YAML Specification</a>.
47 |  |
      * </p>
48 |  |
      */
49 |  |
     public class Composer {
50 |  |
         private final Parser parser;
51 |  |
         private final Resolver resolver;
52 |  |
         private final Map<String, Node> anchors;
53 |  |
         private final Set<Node> recursiveNodes;
54 |  |
55 | 1103 |
         public Composer(Parser parser, Resolver resolver) {
56 | 1103 |
             this.parser = parser;
57 | 1103 |
             this.resolver = resolver;
58 | 1103 |
             this.anchors = new HashMap<String, Node>();
59 | 1103 |
             this.recursiveNodes = new HashSet<Node>();
60 | 1103 |
         }
61 |  |
62 |  |
         /**
63 |  |
          * Checks if further documents are available.
64 |  |
          *
65 |  |
          * @return <code>true</code> if there is at least one more document.
66 |  |
          */
67 |  |
         public boolean checkNode() {
68 |  |
             // Drop the STREAM-START event.
69 | 1155 |
             if (parser.checkEvent(Event.ID.StreamStart)) {
70 | 530 |
                 parser.getEvent();
71 |  |
             }
72 |  |
             // If there are more documents available?
73 | 1151 |
             return !parser.checkEvent(Event.ID.StreamEnd);
74 |  |
         }
75 |  |
76 |  |
         /**
77 |  |
          * Reads and composes the next document.
78 |  |
          *
79 |  |
          * @return The root node of the document or <code>null</code> if no more
80 |  |
          *         documents are available.
81 |  |
          */
82 |  |
         public Node getNode() {
83 |  |
             // Get the root node of the next document.
84 | 572 |
             if (!parser.checkEvent(Event.ID.StreamEnd)) {
85 | 572 |
                 return composeDocument();
86 |  |
             } else {
87 | 0 |
                 return null;
88 |  |
             }
89 |  |
         }
90 |  |
91 |  |
         /**
92 |  |
          * Reads a document from a source that contains only one document.
93 |  |
          * <p>
94 |  |
          * If the stream contains more than one document an exception is thrown.
95 |  |
          * </p>
96 |  |
          *
97 |  |
          * @return The root node of the document or <code>null</code> if no document
98 |  |
          *         is available.
99 |  |
          */
100 |  |
         public Node getSingleNode() {
101 |  |
             // Drop the STREAM-START event.
102 | 572 |
             parser.getEvent();
103 |  |
             // Compose a document if the stream is not empty.
104 | 572 |
             Node document = null;
105 | 572 |
             if (!parser.checkEvent(Event.ID.StreamEnd)) {
106 | 564 |
                 document = composeDocument();
107 |  |
             }
108 |  |
             // Ensure that the stream contains no more documents.
109 | 566 |
             if (!parser.checkEvent(Event.ID.StreamEnd)) {
110 | 5 |
                 Event event = parser.getEvent();
111 | 5 |
                 throw new ComposerException("expected a single document in the stream",
112 |  |
                         document.getStartMark(), "but found another document", event.getStartMark());
113 |  |
             }
114 |  |
             // Drop the STREAM-END event.
115 | 561 |
             parser.getEvent();
116 | 561 |
             return document;
117 |  |
         }
118 |  |
119 |  |
         private Node composeDocument() {
120 |  |
             // Drop the DOCUMENT-START event.
121 | 1136 |
             parser.getEvent();
122 |  |
             // Compose the root node.
123 | 1136 |
             Node node = composeNode(null, null);
124 |  |
             // Drop the DOCUMENT-END event.
125 | 1069 |
             parser.getEvent();
126 | 1069 |
             this.anchors.clear();
127 | 1069 |
             recursiveNodes.clear();
128 | 1069 |
             return node;
129 |  |
         }
130 |  |
131 |  |
         private Node composeNode(Node parent, Object index) {
132 | 387926 |
             recursiveNodes.add(parent);
133 | 387926 |
             if (parser.checkEvent(Event.ID.Alias)) {
134 | 1278 |
                 AliasEvent event = (AliasEvent) parser.getEvent();
135 | 1278 |
                 String anchor = event.getAnchor();
136 | 1278 |
                 if (!anchors.containsKey(anchor)) {
137 | 2 |
                     throw new ComposerException(null, null, "found undefined alias " + anchor,
138 |  |
                             event.getStartMark());
139 |  |
                 }
140 | 1276 |
                 Node result = anchors.get(anchor);
141 | 1276 |
                 if (recursiveNodes.remove(result)) {
142 | 89 |
                     result.setTwoStepsConstruction(true);
143 |  |
                 }
144 | 1276 |
                 return result;
145 |  |
             }
146 | 386624 |
             NodeEvent event = (NodeEvent) parser.peekEvent();
147 | 386624 |
             String anchor = null;
148 | 386624 |
             anchor = event.getAnchor();
149 | 386624 |
             if (anchor != null && anchors.containsKey(anchor)) {
150 | 4 |
                 throw new ComposerException("found duplicate anchor " + anchor + "; first occurence",
151 |  |
                         this.anchors.get(anchor).getStartMark(), "second occurence",
152 |  |
                         event.getStartMark());
153 |  |
             }
154 | 386620 |
             Node node = null;
155 | 386620 |
             if (parser.checkEvent(Event.ID.Scalar)) {
156 | 352552 |
                 node = composeScalarNode(anchor);
157 | 34068 |
             } else if (parser.checkEvent(Event.ID.SequenceStart)) {
158 | 10900 |
                 node = composeSequenceNode(anchor);
159 |  |
             } else {
160 | 23168 |
                 node = composeMappingNode(anchor);
161 |  |
             }
162 | 386564 |
             recursiveNodes.remove(parent);
163 | 386564 |
             return node;
164 |  |
         }
165 |  |
166 |  |
         private Node composeScalarNode(String anchor) {
167 | 352552 |
             ScalarEvent ev = (ScalarEvent) parser.getEvent();
168 | 352552 |
             String tag = ev.getTag();
169 | 352552 |
             boolean resolved = false;
170 |  |
             Tag nodeTag;
171 | 352552 |
             if (tag == null || tag.equals("!")) {
172 | 351507 |
                 nodeTag = resolver.resolve(NodeId.scalar, ev.getValue(), ev.getImplicit().canOmitTagInPlainScalar());
173 | 351507 |
                 resolved = true;
174 |  |
             } else {
175 | 1045 |
                 nodeTag = new Tag(tag);
176 |  |
             }
177 | 352552 |
             Node node = new ScalarNode(nodeTag, resolved, ev.getValue(), ev.getStartMark(),
178 |  |
                     ev.getEndMark(), ev.getStyle());
179 | 352552 |
             if (anchor != null) {
180 | 35 |
                 anchors.put(anchor, node);
181 |  |
             }
182 | 352552 |
             return node;
183 |  |
         }
184 |  |
185 |  |
         private Node composeSequenceNode(String anchor) {
186 | 10900 |
             SequenceStartEvent startEvent = (SequenceStartEvent) parser.getEvent();
187 | 10900 |
             String tag = startEvent.getTag();
188 |  |
             Tag nodeTag;
189 | 10900 |
             boolean resolved = false;
190 | 10900 |
             if (tag == null || tag.equals("!")) {
191 | 10771 |
                 nodeTag = resolver.resolve(NodeId.sequence, null, startEvent.getImplicit());
192 | 10771 |
                 resolved = true;
193 |  |
             } else {
194 | 129 |
                 nodeTag = new Tag(tag);
195 |  |
             }
196 | 10900 |
             final ArrayList<Node> children = new ArrayList<Node>();
197 | 10900 |
             SequenceNode node = new SequenceNode(nodeTag, resolved, children,
198 |  |
                     startEvent.getStartMark(), null, startEvent.getFlowStyle());
199 | 10900 |
             if (anchor != null) {
200 | 10 |
                 anchors.put(anchor, node);
201 |  |
             }
202 | 10900 |
             int index = 0;
203 | 125146 |
             while (!parser.checkEvent(Event.ID.SequenceEnd)) {
204 | 114258 |
                 children.add(composeNode(node, index));
205 | 114246 |
                 index++;
206 |  |
             }
207 | 10867 |
             Event endEvent = parser.getEvent();
208 | 10867 |
             node.setEndMark(endEvent.getEndMark());
209 | 10867 |
             return node;
210 |  |
         }
211 |  |
212 |  |
         private Node composeMappingNode(String anchor) {
213 | 23168 |
             MappingStartEvent startEvent = (MappingStartEvent) parser.getEvent();
214 | 23168 |
             String tag = startEvent.getTag();
215 |  |
             Tag nodeTag;
216 | 23168 |
             boolean resolved = false;
217 | 23168 |
             if (tag == null || tag.equals("!")) {
218 | 21061 |
                 nodeTag = resolver.resolve(NodeId.mapping, null, startEvent.getImplicit());
219 | 21061 |
                 resolved = true;
220 |  |
             } else {
221 | 2107 |
                 nodeTag = new Tag(tag);
222 |  |
             }
223 |  |
224 | 23168 |
             final List<NodeTuple> children = new ArrayList<NodeTuple>();
225 | 23168 |
             MappingNode node = new MappingNode(nodeTag, resolved, children, startEvent.getStartMark(),
226 |  |
                     null, startEvent.getFlowStyle());
227 | 23168 |
             if (anchor != null) {
228 | 1147 |
                 anchors.put(anchor, node);
229 |  |
             }
230 | 159427 |
             while (!parser.checkEvent(Event.ID.MappingEnd)) {
231 | 136266 |
                 Node itemKey = composeNode(node, null);
232 | 136266 |
                 if (itemKey.getTag().equals(Tag.MERGE)) {
233 | 27 |
                     node.setMerged(true);
234 | 136239 |
                 } else if (itemKey.getTag().equals(Tag.VALUE)) {
235 | 2 |
                     itemKey.setTag(Tag.STR);
236 |  |
                 }
237 | 136266 |
                 Node itemValue = composeNode(node, itemKey);
238 | 136259 |
                 children.add(new NodeTuple(itemKey, itemValue));
239 | 136259 |
             }
240 | 23145 |
             Event endEvent = parser.getEvent();
241 | 23145 |
             node.setEndMark(endEvent.getEndMark());
242 | 23145 |
             return node;
243 |  |
         }
244 |  |
     }