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