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