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-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  
 }