/*
* Copyright (c) 2006 Ola Bini
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
* de
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* $Id: SerializerImpl.java,v 1.3 2006/09/30 14:13:35 olabini Exp $
*/
package org.jvyaml;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import org.jvyaml.events.AliasEvent;
import org.jvyaml.events.DocumentEndEvent;
import org.jvyaml.events.DocumentStartEvent;
import org.jvyaml.events.ScalarEvent;
import org.jvyaml.events.MappingEndEvent;
import org.jvyaml.events.MappingStartEvent;
import org.jvyaml.events.SequenceEndEvent;
import org.jvyaml.events.SequenceStartEvent;
import org.jvyaml.events.StreamEndEvent;
import org.jvyaml.events.StreamStartEvent;
import org.jvyaml.nodes.Node;
import org.jvyaml.nodes.CollectionNode;
import org.jvyaml.nodes.MappingNode;
import org.jvyaml.nodes.ScalarNode;
import org.jvyaml.nodes.SequenceNode;
/**
* @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
* @version $Revision: 1.3 $
*/
public class SerializerImpl implements Serializer {
private Emitter emitter;
private Resolver resolver;
private YAMLConfig options;
private boolean useExplicitStart;
private boolean useExplicitEnd;
private int[] useVersion;
private boolean useTags;
private String anchorTemplate;
private Set serializedNodes;
private Map anchors;
private int lastAnchorId;
private boolean closed;
private boolean opened;
public SerializerImpl(final Emitter emitter, final Resolver resolver, final YAMLConfig opts) {
this.emitter = emitter;
this.resolver = resolver;
this.options = opts;
this.useExplicitStart = opts.explicitStart();
this.useExplicitEnd = opts.explicitEnd();
int[] version = new int[2];
if(opts.useVersion()) {
final String v1 = opts.version();
final int index = v1.indexOf('.');
version[0] = Integer.parseInt(v1.substring(0,index));
version[1] = Integer.parseInt(v1.substring(index+1));
} else {
version = null;
}
this.useVersion = version;
this.useTags = opts.useHeader();
this.anchorTemplate = opts.anchorFormat() == null ? "id{0,number,####}" : opts.anchorFormat();
this.serializedNodes = new HashSet();
this.anchors = new HashMap();
this.lastAnchorId = 0;
this.closed = false;
this.opened = false;
}
protected boolean ignoreAnchor(final Node node) {
return false;
}
public void open() throws IOException {
if(!closed && !opened) {
this.emitter.emit(new StreamStartEvent());
this.opened = true;
} else if(closed) {
throw new SerializerException("serializer is closed");
} else {
throw new SerializerException("serializer is already opened");
}
}
public void close() throws IOException {
if(!opened) {
throw new SerializerException("serializer is not opened");
} else if(!closed) {
this.emitter.emit(new StreamEndEvent());
this.closed = true;
this.opened = false;
}
}
public void serialize(final Node node) throws IOException {
if(!this.closed && !this.opened) {
throw new SerializerException("serializer is not opened");
} else if(this.closed) {
throw new SerializerException("serializer is closed");
}
this.emitter.emit(new DocumentStartEvent(this.useExplicitStart,this.useVersion,null));
anchorNode(node);
serializeNode(node,null,null);
this.emitter.emit(new DocumentEndEvent(this.useExplicitEnd));
this.serializedNodes = new HashSet();
this.anchors = new HashMap();
this.lastAnchorId = 0;
}
private void anchorNode(final Node node) {
if(!ignoreAnchor(node)) {
if(this.anchors.containsKey(node)) {
String anchor = (String)this.anchors.get(node);
if(null == anchor) {
anchor = generateAnchor(node);
this.anchors.put(node,anchor);
}
} else {
this.anchors.put(node,null);
if(node instanceof SequenceNode) {
for(final Iterator iter = ((List)node.getValue()).iterator();iter.hasNext();) {
anchorNode((Node)iter.next());
}
} else if(node instanceof MappingNode) {
final Map value = (Map)node.getValue();
for(final Iterator iter = value.keySet().iterator();iter.hasNext();) {
final Node key = (Node)iter.next();
anchorNode(key);
anchorNode((Node)value.get(key));
}
}
}
}
}
private String generateAnchor(final Node node) {
this.lastAnchorId++;
return new MessageFormat(this.anchorTemplate).format(new Object[]{new Integer(this.lastAnchorId)});
}
private void serializeNode(final Node node, final Node parent, final Object index) throws IOException {
final String tAlias = (String)this.anchors.get(node);
if(this.serializedNodes.contains(node) && tAlias != null) {
this.emitter.emit(new AliasEvent(tAlias));
} else {
this.serializedNodes.add(node);
this.resolver.descendResolver(parent,index);
if(node instanceof ScalarNode) {
final String detectedTag = this.resolver.resolve(ScalarNode.class,(String)node.getValue(),new boolean[]{true,false});
final String defaultTag = this.resolver.resolve(ScalarNode.class,(String)node.getValue(),new boolean[]{false,true});
final boolean[] implicit = new boolean[] {false,false};
if(!options.explicitTypes()) {
implicit[0] = node.getTag().equals(detectedTag);
implicit[1] = node.getTag().equals(defaultTag);
}
this.emitter.emit(new ScalarEvent(tAlias,node.getTag(),implicit,(String)node.getValue(),((ScalarNode)node).getStyle()));
} else if(node instanceof SequenceNode) {
final boolean implicit = !options.explicitTypes() && (node.getTag().equals(this.resolver.resolve(SequenceNode.class,null,new boolean[]{true,true})));
this.emitter.emit(new SequenceStartEvent(tAlias,node.getTag(),implicit,((CollectionNode)node).getFlowStyle()));
int ix = 0;
for(final Iterator iter = ((List)node.getValue()).iterator();iter.hasNext();) {
serializeNode((Node)iter.next(),node,new Integer(ix++));
}
this.emitter.emit(new SequenceEndEvent());
} else if(node instanceof MappingNode) {
final boolean implicit = !options.explicitTypes() && (node.getTag().equals(this.resolver.resolve(MappingNode.class,null,new boolean[]{true,true})));
this.emitter.emit(new MappingStartEvent(tAlias,node.getTag(),implicit,((CollectionNode)node).getFlowStyle()));
final Map value = (Map)node.getValue();
for(final Iterator iter = value.keySet().iterator();iter.hasNext();) {
final Node key = (Node)iter.next();
serializeNode(key,node,null);
serializeNode((Node)value.get(key),node,key);
}
this.emitter.emit(new MappingEndEvent());
}
}
}
}// SerializerImpl