View Javadoc

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.serializer;
18  
19  import java.io.IOException;
20  import java.text.NumberFormat;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.yaml.snakeyaml.DumperOptions;
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 Integer[] useVersion;
57      private Map<String, String> useTags;
58      private Set<Node> serializedNodes;
59      private Map<Node, String> anchors;
60      private int lastAnchorId;
61      private Boolean closed;
62      private Tag explicitRoot;
63  
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().getArray();
71          }
72          this.useTags = opts.getTags();
73          this.serializedNodes = new HashSet<Node>();
74          this.anchors = new HashMap<Node, String>();
75          this.lastAnchorId = 0;
76          this.closed = null;
77          this.explicitRoot = rootTag;
78      }
79  
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  
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  
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, null);
113         this.emitter.emit(new DocumentEndEvent(null, null, this.explicitEnd));
114         this.serializedNodes.clear();
115         this.anchors.clear();
116         this.lastAnchorId = 0;
117     }
118 
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 = generateAnchor();
127                 this.anchors.put(node, anchor);
128             }
129         } else {
130             this.anchors.put(node, null);
131             switch (node.getNodeId()) {
132             case sequence:
133                 SequenceNode seqNode = (SequenceNode) node;
134                 List<Node> list = seqNode.getValue();
135                 for (Node item : list) {
136                     anchorNode(item);
137                 }
138                 break;
139             case mapping:
140                 MappingNode mnode = (MappingNode) node;
141                 List<NodeTuple> map = mnode.getValue();
142                 for (NodeTuple object : map) {
143                     Node key = object.getKeyNode();
144                     Node value = object.getValueNode();
145                     anchorNode(key);
146                     anchorNode(value);
147                 }
148                 break;
149             }
150         }
151     }
152 
153     private String generateAnchor() {
154         this.lastAnchorId++;
155         NumberFormat format = NumberFormat.getNumberInstance();
156         format.setMinimumIntegerDigits(3);
157         format.setGroupingUsed(false);
158         String anchorId = format.format(this.lastAnchorId);
159         return "id" + anchorId;
160     }
161 
162     private void serializeNode(Node node, Node parent, Object index) throws IOException {
163         if (node.getNodeId() == NodeId.anchor) {
164             node = ((AnchorNode) node).getRealNode();
165         }
166         String tAlias = this.anchors.get(node);
167         if (this.serializedNodes.contains(node)) {
168             this.emitter.emit(new AliasEvent(tAlias, null, null));
169         } else {
170             this.serializedNodes.add(node);
171             switch (node.getNodeId()) {
172             case scalar:
173                 ScalarNode scalarNode = (ScalarNode) node;
174                 Tag detectedTag = this.resolver.resolve(NodeId.scalar, scalarNode.getValue(), true);
175                 Tag defaultTag = this.resolver.resolve(NodeId.scalar, scalarNode.getValue(), false);
176                 ImplicitTuple tuple = new ImplicitTuple(node.getTag().equals(detectedTag), node
177                         .getTag().equals(defaultTag));
178                 ScalarEvent event = new ScalarEvent(tAlias, node.getTag().getValue(), tuple,
179                         scalarNode.getValue(), null, null, scalarNode.getStyle());
180                 this.emitter.emit(event);
181                 break;
182             case sequence:
183                 SequenceNode seqNode = (SequenceNode) node;
184                 boolean implicitS = (node.getTag().equals(this.resolver.resolve(NodeId.sequence,
185                         null, true)));
186                 this.emitter.emit(new SequenceStartEvent(tAlias, node.getTag().getValue(),
187                         implicitS, null, null, seqNode.getFlowStyle()));
188                 int indexCounter = 0;
189                 List<Node> list = seqNode.getValue();
190                 for (Node item : list) {
191                     serializeNode(item, node, indexCounter);
192                     indexCounter++;
193                 }
194                 this.emitter.emit(new SequenceEndEvent(null, null));
195                 break;
196             default:// instance of MappingNode
197                 Tag implicitTag = this.resolver.resolve(NodeId.mapping, null, true);
198                 boolean implicitM = (node.getTag().equals(implicitTag));
199                 this.emitter.emit(new MappingStartEvent(tAlias, node.getTag().getValue(),
200                         implicitM, null, null, ((CollectionNode) node).getFlowStyle()));
201                 MappingNode mnode = (MappingNode) node;
202                 List<NodeTuple> map = mnode.getValue();
203                 for (NodeTuple row : map) {
204                     Node key = row.getKeyNode();
205                     Node value = row.getValueNode();
206                     serializeNode(key, mnode, null);
207                     serializeNode(value, mnode, key);
208                 }
209                 this.emitter.emit(new MappingEndEvent(null, null));
210             }
211         }
212     }
213 }