Coverage Report - org.yaml.snakeyaml.composer.Composer
 
Classes in this File Line Coverage Branch Coverage Complexity
Composer
99%
101/102
94%
47/50
4.222
 
 1  
 /**
 2  
  * Copyright (c) 2008-2012, 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  
 package org.yaml.snakeyaml.composer;
 17  
 
 18  
 import java.util.ArrayList;
 19  
 import java.util.HashMap;
 20  
 import java.util.HashSet;
 21  
 import java.util.List;
 22  
 import java.util.Map;
 23  
 import java.util.Set;
 24  
 
 25  
 import org.yaml.snakeyaml.events.AliasEvent;
 26  
 import org.yaml.snakeyaml.events.Event;
 27  
 import org.yaml.snakeyaml.events.MappingStartEvent;
 28  
 import org.yaml.snakeyaml.events.NodeEvent;
 29  
 import org.yaml.snakeyaml.events.ScalarEvent;
 30  
 import org.yaml.snakeyaml.events.SequenceStartEvent;
 31  
 import org.yaml.snakeyaml.nodes.MappingNode;
 32  
 import org.yaml.snakeyaml.nodes.Node;
 33  
 import org.yaml.snakeyaml.nodes.NodeId;
 34  
 import org.yaml.snakeyaml.nodes.NodeTuple;
 35  
 import org.yaml.snakeyaml.nodes.ScalarNode;
 36  
 import org.yaml.snakeyaml.nodes.SequenceNode;
 37  
 import org.yaml.snakeyaml.nodes.Tag;
 38  
 import org.yaml.snakeyaml.parser.Parser;
 39  
 import org.yaml.snakeyaml.resolver.Resolver;
 40  
 
 41  
 /**
 42  
  * Creates a node graph from parser events.
 43  
  * <p>
 44  
  * Corresponds to the 'Compose' step as described in chapter 3.1 of the <a
 45  
  * href="http://yaml.org/spec/1.1/">YAML Specification</a>.
 46  
  * </p>
 47  
  */
 48  
 public class Composer {
 49  
     private final Parser parser;
 50  
     private final Resolver resolver;
 51  
     private final Map<String, Node> anchors;
 52  
     private final Set<Node> recursiveNodes;
 53  
 
 54  130066
     public Composer(Parser parser, Resolver resolver) {
 55  130066
         this.parser = parser;
 56  130066
         this.resolver = resolver;
 57  130066
         this.anchors = new HashMap<String, Node>();
 58  130066
         this.recursiveNodes = new HashSet<Node>();
 59  130066
     }
 60  
 
 61  
     /**
 62  
      * Checks if further documents are available.
 63  
      * 
 64  
      * @return <code>true</code> if there is at least one more document.
 65  
      */
 66  
     public boolean checkNode() {
 67  
         // Drop the STREAM-START event.
 68  1171
         if (parser.checkEvent(Event.ID.StreamStart)) {
 69  537
             parser.getEvent();
 70  
         }
 71  
         // If there are more documents available?
 72  1167
         return !parser.checkEvent(Event.ID.StreamEnd);
 73  
     }
 74  
 
 75  
     /**
 76  
      * Reads and composes the next document.
 77  
      * 
 78  
      * @return The root node of the document or <code>null</code> if no more
 79  
      *         documents are available.
 80  
      */
 81  
     public Node getNode() {
 82  
         // Get the root node of the next document.
 83  581
         if (!parser.checkEvent(Event.ID.StreamEnd)) {
 84  581
             return composeDocument();
 85  
         } else {
 86  0
             return null;
 87  
         }
 88  
     }
 89  
 
 90  
     /**
 91  
      * Reads a document from a source that contains only one document.
 92  
      * <p>
 93  
      * If the stream contains more than one document an exception is thrown.
 94  
      * </p>
 95  
      * 
 96  
      * @return The root node of the document or <code>null</code> if no document
 97  
      *         is available.
 98  
      */
 99  
     public Node getSingleNode() {
 100  
         // Drop the STREAM-START event.
 101  129528
         parser.getEvent();
 102  
         // Compose a document if the stream is not empty.
 103  129528
         Node document = null;
 104  129528
         if (!parser.checkEvent(Event.ID.StreamEnd)) {
 105  129520
             document = composeDocument();
 106  
         }
 107  
         // Ensure that the stream contains no more documents.
 108  129522
         if (!parser.checkEvent(Event.ID.StreamEnd)) {
 109  5
             Event event = parser.getEvent();
 110  5
             throw new ComposerException("expected a single document in the stream",
 111  
                     document.getStartMark(), "but found another document", event.getStartMark());
 112  
         }
 113  
         // Drop the STREAM-END event.
 114  129517
         parser.getEvent();
 115  129517
         return document;
 116  
     }
 117  
 
 118  
     private Node composeDocument() {
 119  
         // Drop the DOCUMENT-START event.
 120  130101
         parser.getEvent();
 121  
         // Compose the root node.
 122  130101
         Node node = composeNode(null);
 123  
         // Drop the DOCUMENT-END event.
 124  130034
         parser.getEvent();
 125  130034
         this.anchors.clear();
 126  130034
         recursiveNodes.clear();
 127  130034
         return node;
 128  
     }
 129  
 
 130  
     private Node composeNode(Node parent) {
 131  517046
         recursiveNodes.add(parent);
 132  517046
         if (parser.checkEvent(Event.ID.Alias)) {
 133  1281
             AliasEvent event = (AliasEvent) parser.getEvent();
 134  1281
             String anchor = event.getAnchor();
 135  1281
             if (!anchors.containsKey(anchor)) {
 136  2
                 throw new ComposerException(null, null, "found undefined alias " + anchor,
 137  
                         event.getStartMark());
 138  
             }
 139  1279
             Node result = anchors.get(anchor);
 140  1279
             if (recursiveNodes.remove(result)) {
 141  89
                 result.setTwoStepsConstruction(true);
 142  
             }
 143  1279
             return result;
 144  
         }
 145  515741
         NodeEvent event = (NodeEvent) parser.peekEvent();
 146  515741
         String anchor = null;
 147  515741
         anchor = event.getAnchor();
 148  515741
         if (anchor != null && anchors.containsKey(anchor)) {
 149  4
             throw new ComposerException("found duplicate anchor " + anchor + "; first occurence",
 150  
                     this.anchors.get(anchor).getStartMark(), "second occurence",
 151  
                     event.getStartMark());
 152  
         }
 153  515737
         Node node = null;
 154  515737
         if (parser.checkEvent(Event.ID.Scalar)) {
 155  481631
             node = composeScalarNode(anchor);
 156  34106
         } else if (parser.checkEvent(Event.ID.SequenceStart)) {
 157  10905
             node = composeSequenceNode(anchor);
 158  
         } else {
 159  23201
             node = composeMappingNode(anchor);
 160  
         }
 161  515681
         recursiveNodes.remove(parent);
 162  515681
         return node;
 163  
     }
 164  
 
 165  
     private Node composeScalarNode(String anchor) {
 166  481631
         ScalarEvent ev = (ScalarEvent) parser.getEvent();
 167  481631
         String tag = ev.getTag();
 168  481631
         boolean resolved = false;
 169  
         Tag nodeTag;
 170  481631
         if (tag == null || tag.equals("!")) {
 171  480586
             nodeTag = resolver.resolve(NodeId.scalar, ev.getValue(), ev.getImplicit()
 172  
                     .canOmitTagInPlainScalar());
 173  480586
             resolved = true;
 174  
         } else {
 175  1045
             nodeTag = new Tag(tag);
 176  
         }
 177  481631
         Node node = new ScalarNode(nodeTag, resolved, ev.getValue(), ev.getStartMark(),
 178  
                 ev.getEndMark(), ev.getStyle());
 179  481631
         if (anchor != null) {
 180  35
             anchors.put(anchor, node);
 181  
         }
 182  481631
         return node;
 183  
     }
 184  
 
 185  
     private Node composeSequenceNode(String anchor) {
 186  10905
         SequenceStartEvent startEvent = (SequenceStartEvent) parser.getEvent();
 187  10905
         String tag = startEvent.getTag();
 188  
         Tag nodeTag;
 189  10905
         boolean resolved = false;
 190  10905
         if (tag == null || tag.equals("!")) {
 191  10776
             nodeTag = resolver.resolve(NodeId.sequence, null, startEvent.getImplicit());
 192  10776
             resolved = true;
 193  
         } else {
 194  129
             nodeTag = new Tag(tag);
 195  
         }
 196  10905
         final ArrayList<Node> children = new ArrayList<Node>();
 197  10905
         SequenceNode node = new SequenceNode(nodeTag, resolved, children,
 198  
                 startEvent.getStartMark(), null, startEvent.getFlowStyle());
 199  10905
         if (anchor != null) {
 200  10
             anchors.put(anchor, node);
 201  
         }
 202  10905
         int index = 0;
 203  125168
         while (!parser.checkEvent(Event.ID.SequenceEnd)) {
 204  114275
             children.add(composeNode(node));
 205  114263
             index++;
 206  
         }
 207  10872
         Event endEvent = parser.getEvent();
 208  10872
         node.setEndMark(endEvent.getEndMark());
 209  10872
         return node;
 210  
     }
 211  
 
 212  
     private Node composeMappingNode(String anchor) {
 213  23201
         MappingStartEvent startEvent = (MappingStartEvent) parser.getEvent();
 214  23201
         String tag = startEvent.getTag();
 215  
         Tag nodeTag;
 216  23201
         boolean resolved = false;
 217  23201
         if (tag == null || tag.equals("!")) {
 218  21083
             nodeTag = resolver.resolve(NodeId.mapping, null, startEvent.getImplicit());
 219  21083
             resolved = true;
 220  
         } else {
 221  2118
             nodeTag = new Tag(tag);
 222  
         }
 223  
 
 224  23201
         final List<NodeTuple> children = new ArrayList<NodeTuple>();
 225  23201
         MappingNode node = new MappingNode(nodeTag, resolved, children, startEvent.getStartMark(),
 226  
                 null, startEvent.getFlowStyle());
 227  23201
         if (anchor != null) {
 228  1155
             anchors.put(anchor, node);
 229  
         }
 230  159529
         while (!parser.checkEvent(Event.ID.MappingEnd)) {
 231  136335
             Node itemKey = composeNode(node);
 232  136335
             if (itemKey.getTag().equals(Tag.MERGE)) {
 233  30
                 node.setMerged(true);
 234  136305
             } else if (itemKey.getTag().equals(Tag.VALUE)) {
 235  2
                 itemKey.setTag(Tag.STR);
 236  
             }
 237  136335
             Node itemValue = composeNode(node);
 238  136328
             children.add(new NodeTuple(itemKey, itemValue));
 239  136328
         }
 240  23178
         Event endEvent = parser.getEvent();
 241  23178
         node.setEndMark(endEvent.getEndMark());
 242  23178
         return node;
 243  
     }
 244  
 }