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 }