001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2007 Ola Bini <ola@ologix.com>
015: *
016: * Alternatively, the contents of this file may be used under the terms of
017: * either of the GNU General Public License Version 2 or later (the "GPL"),
018: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
019: * in which case the provisions of the GPL or the LGPL are applicable instead
020: * of those above. If you wish to allow use of your version of this file only
021: * under the terms of either the GPL or the LGPL, and not to allow others to
022: * use your version of this file under the terms of the CPL, indicate your
023: * decision by deleting the provisions above and replace them with the notice
024: * and other provisions required by the GPL or the LGPL. If you do not delete
025: * the provisions above, a recipient may use your version of this file under
026: * the terms of any one of the CPL, the GPL or the LGPL.
027: ***** END LICENSE BLOCK *****/package org.jvyamlb;
028:
029: import java.io.IOException;
030:
031: import java.text.MessageFormat;
032:
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Map;
036: import java.util.IdentityHashMap;
037: import java.util.HashMap;
038: import java.util.Set;
039: import java.util.HashSet;
040:
041: import org.jvyamlb.events.AliasEvent;
042: import org.jvyamlb.events.DocumentEndEvent;
043: import org.jvyamlb.events.DocumentStartEvent;
044: import org.jvyamlb.events.ScalarEvent;
045: import org.jvyamlb.events.MappingEndEvent;
046: import org.jvyamlb.events.MappingStartEvent;
047: import org.jvyamlb.events.SequenceEndEvent;
048: import org.jvyamlb.events.SequenceStartEvent;
049: import org.jvyamlb.events.StreamEndEvent;
050: import org.jvyamlb.events.StreamStartEvent;
051:
052: import org.jvyamlb.nodes.Node;
053: import org.jvyamlb.nodes.LinkNode;
054: import org.jvyamlb.nodes.CollectionNode;
055: import org.jvyamlb.nodes.MappingNode;
056: import org.jvyamlb.nodes.ScalarNode;
057: import org.jvyamlb.nodes.SequenceNode;
058:
059: import org.jruby.util.ByteList;
060:
061: /**
062: * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
063: */
064: public class SerializerImpl implements Serializer {
065: private Emitter emitter;
066: private Resolver resolver;
067: private YAMLConfig options;
068: private boolean useExplicitStart;
069: private boolean useExplicitEnd;
070: private int[] useVersion;
071: private boolean useTags;
072: private String anchorTemplate;
073: private Map serializedNodes;
074: private Map anchors;
075: private int lastAnchorId;
076: private boolean closed;
077: private boolean opened;
078:
079: public SerializerImpl(final Emitter emitter,
080: final Resolver resolver, final YAMLConfig opts) {
081: this .emitter = emitter;
082: this .resolver = resolver;
083: this .options = opts;
084: this .useExplicitStart = opts.explicitStart();
085: this .useExplicitEnd = opts.explicitEnd();
086: int[] version = new int[2];
087: if (opts.useVersion()) {
088: final String v1 = opts.version();
089: final int index = v1.indexOf('.');
090: version[0] = Integer.parseInt(v1.substring(0, index));
091: version[1] = Integer.parseInt(v1.substring(index + 1));
092: } else {
093: version = null;
094: }
095: this .useVersion = version;
096: this .useTags = opts.useHeader();
097: this .anchorTemplate = opts.anchorFormat() == null ? "id{0,number,000}"
098: : opts.anchorFormat();
099: this .serializedNodes = new IdentityHashMap();
100: this .anchors = new IdentityHashMap();
101: this .lastAnchorId = 0;
102: this .closed = false;
103: this .opened = false;
104: }
105:
106: protected boolean ignoreAnchor(final Node node) {
107: return false;
108: }
109:
110: public void open() throws IOException {
111: if (!closed && !opened) {
112: this .emitter.emit(new StreamStartEvent());
113: this .opened = true;
114: } else if (closed) {
115: throw new SerializerException("serializer is closed");
116: } else {
117: throw new SerializerException(
118: "serializer is already opened");
119: }
120: }
121:
122: public void close() throws IOException {
123: if (!opened) {
124: throw new SerializerException("serializer is not opened");
125: } else if (!closed) {
126: this .emitter.emit(new StreamEndEvent());
127: this .closed = true;
128: this .opened = false;
129: }
130: }
131:
132: public void serialize(final Node node) throws IOException {
133: if (!this .closed && !this .opened) {
134: throw new SerializerException("serializer is not opened");
135: } else if (this .closed) {
136: throw new SerializerException("serializer is closed");
137: }
138: this .emitter.emit(new DocumentStartEvent(this .useExplicitStart,
139: this .useVersion, null));
140: anchorNode(node);
141: serializeNode(node, null, null);
142: this .emitter.emit(new DocumentEndEvent(this .useExplicitEnd));
143: this .serializedNodes = new IdentityHashMap();
144: this .anchors = new IdentityHashMap();
145: this .lastAnchorId = 0;
146: }
147:
148: private void anchorNode(Node node) {
149: while (node instanceof LinkNode) {
150: node = ((LinkNode) node).getAnchor();
151: }
152: if (!ignoreAnchor(node)) {
153: if (this .anchors.containsKey(node)) {
154: String anchor = (String) this .anchors.get(node);
155: if (null == anchor) {
156: anchor = generateAnchor(node);
157: this .anchors.put(node, anchor);
158: }
159: } else {
160: this .anchors.put(node, null);
161: if (node instanceof SequenceNode) {
162: for (final Iterator iter = ((List) node.getValue())
163: .iterator(); iter.hasNext();) {
164: anchorNode((Node) iter.next());
165: }
166: } else if (node instanceof MappingNode) {
167: final Map value = (Map) node.getValue();
168: for (final Iterator iter = value.entrySet()
169: .iterator(); iter.hasNext();) {
170: final Map.Entry me = (Map.Entry) iter.next();
171: anchorNode((Node) me.getKey());
172: anchorNode((Node) me.getValue());
173: }
174: }
175: }
176: }
177: }
178:
179: private String generateAnchor(final Node node) {
180: this .lastAnchorId++;
181: return new MessageFormat(this .anchorTemplate)
182: .format(new Object[] { new Integer(this .lastAnchorId) });
183: }
184:
185: private void serializeNode(Node node, final Node parent,
186: final Object index) throws IOException {
187: while (node instanceof LinkNode) {
188: node = ((LinkNode) node).getAnchor();
189: }
190: String tAlias = (String) this .anchors.get(node);
191: if (this .serializedNodes.containsKey(node) && tAlias != null) {
192: this .emitter.emit(new AliasEvent(tAlias));
193: } else {
194: this .serializedNodes.put(node, null);
195: this .resolver.descendResolver(parent, index);
196: if (node instanceof ScalarNode) {
197: final String detectedTag = this .resolver.resolve(
198: ScalarNode.class, (ByteList) node.getValue(),
199: new boolean[] { true, false });
200: final String defaultTag = this .resolver.resolve(
201: ScalarNode.class, (ByteList) node.getValue(),
202: new boolean[] { false, true });
203: final boolean[] implicit = new boolean[] { false, false };
204: if (!options.explicitTypes()) {
205: implicit[0] = node.getTag().equals(detectedTag);
206: implicit[1] = node.getTag().equals(defaultTag);
207: }
208: this .emitter.emit(new ScalarEvent(tAlias,
209: node.getTag(), implicit, (ByteList) node
210: .getValue(), ((ScalarNode) node)
211: .getStyle()));
212: } else if (node instanceof SequenceNode) {
213: final boolean implicit = !options.explicitTypes()
214: && (node.getTag().equals(this .resolver.resolve(
215: SequenceNode.class, null,
216: new boolean[] { true, true })));
217: this .emitter.emit(new SequenceStartEvent(tAlias, node
218: .getTag(), implicit, ((CollectionNode) node)
219: .getFlowStyle()));
220: int ix = 0;
221: for (final Iterator iter = ((List) node.getValue())
222: .iterator(); iter.hasNext();) {
223: serializeNode((Node) iter.next(), node,
224: new Integer(ix++));
225: }
226: this .emitter.emit(new SequenceEndEvent());
227: } else if (node instanceof MappingNode) {
228: final boolean implicit = !options.explicitTypes()
229: && (node.getTag().equals(this .resolver.resolve(
230: MappingNode.class, null, new boolean[] {
231: true, true })));
232: this .emitter.emit(new MappingStartEvent(tAlias, node
233: .getTag(), implicit, ((CollectionNode) node)
234: .getFlowStyle()));
235: final Map value = (Map) node.getValue();
236: for (final Iterator iter = value.entrySet().iterator(); iter
237: .hasNext();) {
238: final Map.Entry entry = (Map.Entry) iter.next();
239: final Node key = (Node) entry.getKey();
240: final Node val = (Node) entry.getValue();
241: serializeNode(key, node, null);
242: serializeNode(val, node, key);
243: }
244: this .emitter.emit(new MappingEndEvent());
245: }
246: }
247: }
248: }// SerializerImpl
|