• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.serializer;
15 
16 import java.io.IOException;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import org.yaml.snakeyaml.DumperOptions;
23 import org.yaml.snakeyaml.DumperOptions.Version;
24 import org.yaml.snakeyaml.comments.CommentLine;
25 import org.yaml.snakeyaml.emitter.Emitable;
26 import org.yaml.snakeyaml.events.AliasEvent;
27 import org.yaml.snakeyaml.events.CommentEvent;
28 import org.yaml.snakeyaml.events.DocumentEndEvent;
29 import org.yaml.snakeyaml.events.DocumentStartEvent;
30 import org.yaml.snakeyaml.events.ImplicitTuple;
31 import org.yaml.snakeyaml.events.MappingEndEvent;
32 import org.yaml.snakeyaml.events.MappingStartEvent;
33 import org.yaml.snakeyaml.events.ScalarEvent;
34 import org.yaml.snakeyaml.events.SequenceEndEvent;
35 import org.yaml.snakeyaml.events.SequenceStartEvent;
36 import org.yaml.snakeyaml.events.StreamEndEvent;
37 import org.yaml.snakeyaml.events.StreamStartEvent;
38 import org.yaml.snakeyaml.nodes.AnchorNode;
39 import org.yaml.snakeyaml.nodes.MappingNode;
40 import org.yaml.snakeyaml.nodes.Node;
41 import org.yaml.snakeyaml.nodes.NodeId;
42 import org.yaml.snakeyaml.nodes.NodeTuple;
43 import org.yaml.snakeyaml.nodes.ScalarNode;
44 import org.yaml.snakeyaml.nodes.SequenceNode;
45 import org.yaml.snakeyaml.nodes.Tag;
46 import org.yaml.snakeyaml.resolver.Resolver;
47 
48 public final class Serializer {
49 
50   private final Emitable emitter;
51   private final Resolver resolver;
52   private final boolean explicitStart;
53   private final boolean explicitEnd;
54   private Version useVersion;
55   private final Map<String, String> useTags;
56   private final Set<Node> serializedNodes;
57   private final Map<Node, String> anchors;
58   private final AnchorGenerator anchorGenerator;
59   private Boolean closed;
60   private final Tag explicitRoot;
61 
Serializer(Emitable emitter, Resolver resolver, DumperOptions opts, Tag rootTag)62   public Serializer(Emitable emitter, Resolver resolver, DumperOptions opts, Tag rootTag) {
63     this.emitter = emitter;
64     this.resolver = resolver;
65     this.explicitStart = opts.isExplicitStart();
66     this.explicitEnd = opts.isExplicitEnd();
67     if (opts.getVersion() != null) {
68       this.useVersion = opts.getVersion();
69     }
70     this.useTags = opts.getTags();
71     this.serializedNodes = new HashSet<Node>();
72     this.anchors = new HashMap<Node, String>();
73     this.anchorGenerator = opts.getAnchorGenerator();
74     this.closed = null;
75     this.explicitRoot = rootTag;
76   }
77 
open()78   public void open() throws IOException {
79     if (closed == null) {
80       this.emitter.emit(new StreamStartEvent(null, null));
81       this.closed = Boolean.FALSE;
82     } else if (Boolean.TRUE.equals(closed)) {
83       throw new SerializerException("serializer is closed");
84     } else {
85       throw new SerializerException("serializer is already opened");
86     }
87   }
88 
close()89   public void close() throws IOException {
90     if (closed == null) {
91       throw new SerializerException("serializer is not opened");
92     } else if (!Boolean.TRUE.equals(closed)) {
93       this.emitter.emit(new StreamEndEvent(null, null));
94       this.closed = Boolean.TRUE;
95       // release unused resources
96       this.serializedNodes.clear();
97       this.anchors.clear();
98     }
99   }
100 
serialize(Node node)101   public void serialize(Node node) throws IOException {
102     if (closed == null) {
103       throw new SerializerException("serializer is not opened");
104     } else if (closed) {
105       throw new SerializerException("serializer is closed");
106     }
107     this.emitter
108         .emit(new DocumentStartEvent(null, null, this.explicitStart, this.useVersion, useTags));
109     anchorNode(node);
110     if (explicitRoot != null) {
111       node.setTag(explicitRoot);
112     }
113     serializeNode(node, null);
114     this.emitter.emit(new DocumentEndEvent(null, null, this.explicitEnd));
115     this.serializedNodes.clear();
116     this.anchors.clear();
117   }
118 
anchorNode(Node node)119   private void anchorNode(Node node) {
120     if (node.getNodeId() == NodeId.anchor) {
121       node = ((AnchorNode) node).getRealNode();
122     }
123     if (this.anchors.containsKey(node)) {
124       String anchor = this.anchors.get(node);
125       if (null == anchor) {
126         anchor = this.anchorGenerator.nextAnchor(node);
127         this.anchors.put(node, anchor);
128       }
129     } else {
130       this.anchors.put(node,
131           node.getAnchor() != null ? this.anchorGenerator.nextAnchor(node) : null);
132       switch (node.getNodeId()) {
133         case sequence:
134           SequenceNode seqNode = (SequenceNode) node;
135           List<Node> list = seqNode.getValue();
136           for (Node item : list) {
137             anchorNode(item);
138           }
139           break;
140         case mapping:
141           MappingNode mnode = (MappingNode) node;
142           List<NodeTuple> map = mnode.getValue();
143           for (NodeTuple object : map) {
144             Node key = object.getKeyNode();
145             Node value = object.getValueNode();
146             anchorNode(key);
147             anchorNode(value);
148           }
149           break;
150       }
151     }
152   }
153 
154   // parent Node is not used but might be used in the future
serializeNode(Node node, Node parent)155   private void serializeNode(Node node, Node parent) throws IOException {
156     if (node.getNodeId() == NodeId.anchor) {
157       node = ((AnchorNode) node).getRealNode();
158     }
159     String tAlias = this.anchors.get(node);
160     if (this.serializedNodes.contains(node)) {
161       this.emitter.emit(new AliasEvent(tAlias, null, null));
162     } else {
163       this.serializedNodes.add(node);
164       switch (node.getNodeId()) {
165         case scalar:
166           ScalarNode scalarNode = (ScalarNode) node;
167           serializeComments(node.getBlockComments());
168           Tag detectedTag = this.resolver.resolve(NodeId.scalar, scalarNode.getValue(), true);
169           Tag defaultTag = this.resolver.resolve(NodeId.scalar, scalarNode.getValue(), false);
170           ImplicitTuple tuple = new ImplicitTuple(node.getTag().equals(detectedTag),
171               node.getTag().equals(defaultTag));
172           ScalarEvent event = new ScalarEvent(tAlias, node.getTag().getValue(), tuple,
173               scalarNode.getValue(), null, null, scalarNode.getScalarStyle());
174           this.emitter.emit(event);
175           serializeComments(node.getInLineComments());
176           serializeComments(node.getEndComments());
177           break;
178         case sequence:
179           SequenceNode seqNode = (SequenceNode) node;
180           serializeComments(node.getBlockComments());
181           boolean implicitS =
182               node.getTag().equals(this.resolver.resolve(NodeId.sequence, null, true));
183           this.emitter.emit(new SequenceStartEvent(tAlias, node.getTag().getValue(), implicitS,
184               null, null, seqNode.getFlowStyle()));
185           List<Node> list = seqNode.getValue();
186           for (Node item : list) {
187             serializeNode(item, node);
188           }
189           this.emitter.emit(new SequenceEndEvent(null, null));
190           serializeComments(node.getInLineComments());
191           serializeComments(node.getEndComments());
192           break;
193         default:// instance of MappingNode
194           serializeComments(node.getBlockComments());
195           Tag implicitTag = this.resolver.resolve(NodeId.mapping, null, true);
196           boolean implicitM = node.getTag().equals(implicitTag);
197           MappingNode mnode = (MappingNode) node;
198           List<NodeTuple> map = mnode.getValue();
199           if (mnode.getTag() != Tag.COMMENT) {
200             this.emitter.emit(new MappingStartEvent(tAlias, mnode.getTag().getValue(), implicitM,
201                 null, null, mnode.getFlowStyle()));
202             for (NodeTuple row : map) {
203               Node key = row.getKeyNode();
204               Node value = row.getValueNode();
205               serializeNode(key, mnode);
206               serializeNode(value, mnode);
207             }
208             this.emitter.emit(new MappingEndEvent(null, null));
209             serializeComments(node.getInLineComments());
210             serializeComments(node.getEndComments());
211           }
212       }
213     }
214   }
215 
serializeComments(List<CommentLine> comments)216   private void serializeComments(List<CommentLine> comments) throws IOException {
217     if (comments == null) {
218       return;
219     }
220     for (CommentLine line : comments) {
221       CommentEvent commentEvent = new CommentEvent(line.getCommentType(), line.getValue(),
222           line.getStartMark(), line.getEndMark());
223       this.emitter.emit(commentEvent);
224     }
225   }
226 }
227