/*
* 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:
*
* 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: RepresenterImpl.java,v 1.2 2006/09/24 16:32:35 olabini Exp $
*/
package org.jvyaml;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Set;
import java.util.Date;
import java.util.Calendar;
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.2 $
*/
public class RepresenterImpl implements Representer {
private final Serializer serializer;
private final char defaultStyle;
private final Map representedObjects;
public RepresenterImpl(final Serializer serializer, final YAMLConfig opts) {
this.serializer = serializer;
this.defaultStyle = opts.useDouble() ? '"' : (opts.useSingle() ? '\'' : 0);
this.representedObjects = new HashMap();
}
private Node representData(final Object data) throws IOException {
String aliasKey = null;
Node node = null;
if(!ignoreAliases(data)) {
aliasKey = ""+System.identityHashCode(data);
}
if(null != aliasKey) {
if(this.representedObjects.containsKey(aliasKey)) {
node = (Node)this.representedObjects.get(aliasKey);
if(null == node) {
throw new RepresenterException("recursive objects are not allowed: " + data);
}
return node;
}
this.representedObjects.put(aliasKey,null);
}
node = getNodeCreatorFor(data).toYamlNode(this);
if(aliasKey != null) {
this.representedObjects.put(aliasKey,node);
}
return node;
}
public Node scalar(final String tag, final String value, char style) throws IOException {
return representScalar(tag,value,style);
}
public Node representScalar(final String tag, final String value, char style) throws IOException {
char realStyle = style == 0 ? this.defaultStyle : style;
return new ScalarNode(tag,value,style);
}
public Node seq(final String tag, final List sequence, final boolean flowStyle) throws IOException {
return representSequence(tag,sequence,flowStyle);
}
public Node representSequence(final String tag, final List sequence, final boolean flowStyle) throws IOException {
List value = new ArrayList(sequence.size());
for(final Iterator iter = sequence.iterator();iter.hasNext();) {
value.add(representData(iter.next()));
}
return new SequenceNode(tag,value,flowStyle);
}
public Node map(final String tag, final Map mapping, final boolean flowStyle) throws IOException {
return representMapping(tag,mapping,flowStyle);
}
public Node representMapping(final String tag, final Map mapping, final boolean flowStyle) throws IOException {
Map value = new HashMap();
for(final Iterator iter = mapping.keySet().iterator();iter.hasNext();) {
final Object itemKey = iter.next();
final Object itemValue = mapping.get(itemKey);
value.put(representData(itemKey),representData(itemValue));
}
return new MappingNode(tag,value,flowStyle);
}
public void represent(final Object data) throws IOException {
Node node = representData(data);
this.serializer.serialize(node);
this.representedObjects.clear();
}
protected boolean ignoreAliases(final Object data) {
return false;
}
protected YAMLNodeCreator getNodeCreatorFor(final Object data) {
if(data instanceof YAMLNodeCreator) {
return (YAMLNodeCreator)data;
} else if(data instanceof Map) {
return new MappingYAMLNodeCreator(data);
} else if(data instanceof List) {
return new SequenceYAMLNodeCreator(data);
} else if(data instanceof Set) {
return new SetYAMLNodeCreator(data);
} else if(data instanceof Date) {
return new DateYAMLNodeCreator(data);
} else if(data instanceof String) {
return new StringYAMLNodeCreator(data);
} else if(data instanceof Number) {
return new NumberYAMLNodeCreator(data);
} else if(data instanceof Boolean) {
return new ScalarYAMLNodeCreator("tag:yaml.org,2002:bool",data);
} else if(data == null) {
return new ScalarYAMLNodeCreator("tag:yaml.org,2002:null","");
} else if(data.getClass().isArray()) {
return new ArrayYAMLNodeCreator(data);
} else { // Fallback, handles JavaBeans and other
return new JavaBeanYAMLNodeCreator(data);
}
}
public static class DateYAMLNodeCreator implements YAMLNodeCreator {
private final Date data;
public DateYAMLNodeCreator(final Object data) {
this.data = (Date)data;
}
public String taguri() {
return "tag:yaml.org,2002:timestamp";
}
private static DateFormat dateOutput = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
private static DateFormat dateOutputUsec = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z");
public Node toYamlNode(final Representer representer) throws IOException {
final Calendar c = Calendar.getInstance();
c.setTime(data);
String out = null;
if(c.get(Calendar.MILLISECOND) != 0) {
out = dateOutputUsec.format(data);
} else {
out = dateOutput.format(data);
}
out = out.substring(0, 23) + ":" + out.substring(23);
return representer.scalar(taguri(), out, (char)0);
}
}
public static class SetYAMLNodeCreator implements YAMLNodeCreator {
private final Set data;
public SetYAMLNodeCreator(final Object data) {
this.data = (Set)data;
}
public String taguri() {
return "tag:yaml.org,2002:set";
}
public Node toYamlNode(final Representer representer) throws IOException {
final Map entries = new HashMap();
for(final Iterator iter = data.iterator();iter.hasNext();) {
entries.put(iter.next(),null);
}
return representer.map(taguri(), entries, false);
}
}
public static class ArrayYAMLNodeCreator implements YAMLNodeCreator {
private final Object data;
public ArrayYAMLNodeCreator(final Object data) {
this.data = data;
}
public String taguri() {
return "tag:yaml.org,2002:seq";
}
public Node toYamlNode(final Representer representer) throws IOException {
final int l = java.lang.reflect.Array.getLength(data);
final List lst = new ArrayList(l);
for(int i=0;i<l;i++) {
lst.add(java.lang.reflect.Array.get(data,i));
}
return representer.seq(taguri(), lst, false);
}
}
public static class NumberYAMLNodeCreator implements YAMLNodeCreator {
private final Number data;
public NumberYAMLNodeCreator(final Object data) {
this.data = (Number)data;
}
public String taguri() {
if(data instanceof Float || data instanceof Double || data instanceof java.math.BigDecimal) {
return "tag:yaml.org,2002:float";
} else {
return "tag:yaml.org,2002:int";
}
}
public Node toYamlNode(Representer representer) throws IOException {
String str = data.toString();
if(str.equals("Infinity")) {
str = ".inf";
} else if(str.equals("-Infinity")) {
str = "-.inf";
} else if(str.equals("NaN")) {
str = ".nan";
}
return representer.scalar(taguri(), str, (char)0);
}
}
public static class ScalarYAMLNodeCreator implements YAMLNodeCreator {
private final String tag;
private final Object data;
public ScalarYAMLNodeCreator(final String tag, final Object data) {
this.tag = tag;
this.data = data;
}
public String taguri() {
return this.tag;
}
public Node toYamlNode(Representer representer) throws IOException {
return representer.scalar(taguri(), data.toString(), (char)0);
}
}
public static class StringYAMLNodeCreator implements YAMLNodeCreator {
private final Object data;
public StringYAMLNodeCreator(final Object data) {
this.data = data;
}
public String taguri() {
if(data instanceof String) {
return "tag:yaml.org,2002:str";
} else {
return "tag:yaml.org,2002:str:"+data.getClass().getName();
}
}
public Node toYamlNode(Representer representer) throws IOException {
return representer.scalar(taguri(), data.toString(), (char)0);
}
}
public static class SequenceYAMLNodeCreator implements YAMLNodeCreator {
private final List data;
public SequenceYAMLNodeCreator(final Object data) {
this.data = (List)data;
}
public String taguri() {
if(data instanceof ArrayList) {
return "tag:yaml.org,2002:seq";
} else {
return "tag:yaml.org,2002:seq:"+data.getClass().getName();
}
}
public Node toYamlNode(Representer representer) throws IOException {
return representer.seq(taguri(), data, false);
}
}
public static class MappingYAMLNodeCreator implements YAMLNodeCreator {
private final Map data;
public MappingYAMLNodeCreator(final Object data) {
this.data = (Map)data;
}
public String taguri() {
if(data instanceof HashMap) {
return "tag:yaml.org,2002:map";
} else {
return "tag:yaml.org,2002:map:"+data.getClass().getName();
}
}
public Node toYamlNode(Representer representer) throws IOException {
return representer.map(taguri(), data, false);
}
}
public static class JavaBeanYAMLNodeCreator implements YAMLNodeCreator {
private final Object data;
public JavaBeanYAMLNodeCreator(final Object data) {
this.data = data;
}
public String taguri() {
return "!java/object:" + data.getClass().getName();
}
public Node toYamlNode(Representer representer) throws IOException {
final Map values = new HashMap();
final Method[] ems = data.getClass().getMethods();
for(int i=0,j=ems.length;i<j;i++) {
if(ems[i].getParameterTypes().length == 0) {
final String name = ems[i].getName();
if(name.equals("getClass")) {
continue;
}
String pname = null;
if(name.startsWith("get")) {
pname = "" + Character.toLowerCase(name.charAt(3)) + name.substring(4);
} else if(name.startsWith("is")) {
pname = "" + Character.toLowerCase(name.charAt(2)) + name.substring(3);
}
if(null != pname) {
try {
values.put(pname, ems[i].invoke(data,null));
} catch(final Exception exe) {
values.put(pname, null);
}
}
}
}
return representer.map(taguri(),values,false);
}
}
public static void main(final String[] args) throws IOException {
final YAMLConfig cfg = YAML.config();
final Serializer s = new SerializerImpl(new EmitterImpl(new java.io.OutputStreamWriter(System.out),cfg),new ResolverImpl(),cfg);
s.open();
final Representer r = new RepresenterImpl(s, cfg);
final Map test1 = new HashMap();
final List test1Val = new LinkedList();
test1Val.add("hello");
test1Val.add(Boolean.TRUE);
test1Val.add(new Integer(31337));
test1.put("val1",test1Val);
final List test2Val = new ArrayList();
test2Val.add("hello");
test2Val.add(Boolean.FALSE);
test2Val.add(new Integer(31337));
test1.put("val2",test2Val);
test1.put("afsdf", "hmm");
TestJavaBean bean1 = new TestJavaBean();
bean1.setName("Ola");
bean1.setSurName("Bini");
bean1.setAge(24);
test1.put(new Integer(25),bean1);
r.represent(test1);
s.close();
}
private static class TestJavaBean implements Serializable {
private String val1;
private String val2;
private int val3;
public TestJavaBean() {
}
public void setName(final String name) {
this.val1 = name;
}
public String getName() {
return this.val1;
}
public void setSurName(final String sname) {
this.val2 = sname;
}
public String getSurName() {
return this.val2;
}
public void setAge(final int age) {
this.val3 = age;
}
public int getAge() {
return this.val3;
}
}
}// RepresenterImpl