286185 JSONPojoConvertorFactoryTest

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@689 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Greg Wilkins 2009-08-11 13:10:52 +00:00
parent 27d0dde15b
commit 3def0eb7ab
3 changed files with 528 additions and 53 deletions

View File

@ -23,6 +23,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.Loader;
@ -70,11 +71,10 @@ import org.eclipse.jetty.util.log.Log;
*/
public class JSON
{
private static JSON __default = new JSON();
public final static JSON DEFAULT = new JSON();
private Map _convertors=new HashMap();
private int _stringBufferSize=256;
private Map<String,Convertor> _convertors=new ConcurrentHashMap<String,Convertor>();
private int _stringBufferSize=256;
public JSON()
{
@ -89,8 +89,6 @@ public class JSON
return _stringBufferSize;
}
/* ------------------------------------------------------------ */
/**
* @param stringBufferSize the initial stringBuffer size to use when creating JSON strings (default 256)
@ -99,10 +97,8 @@ public class JSON
{
_stringBufferSize=stringBufferSize;
}
/* ------------------------------------------------------------ */
/**
* Register a {@link Convertor} for a class or interface.
* @param forClass The class or interface that the convertor applies to
@ -110,58 +106,65 @@ public class JSON
*/
public static void registerConvertor(Class forClass, Convertor convertor)
{
__default.addConvertor(forClass,convertor);
DEFAULT.addConvertor(forClass,convertor);
}
/* ------------------------------------------------------------ */
public static JSON getDefault()
{
return __default;
return DEFAULT;
}
/* ------------------------------------------------------------ */
@Deprecated
public static void setDefault(JSON json)
{
__default=json;
}
/* ------------------------------------------------------------ */
public static String toString(Object object)
{
StringBuffer buffer=new StringBuffer(__default.getStringBufferSize());
StringBuffer buffer=new StringBuffer(DEFAULT.getStringBufferSize());
synchronized (buffer)
{
__default.append(buffer,object);
DEFAULT.append(buffer,object);
return buffer.toString();
}
}
/* ------------------------------------------------------------ */
public static String toString(Map object)
{
StringBuffer buffer=new StringBuffer(__default.getStringBufferSize());
StringBuffer buffer=new StringBuffer(DEFAULT.getStringBufferSize());
synchronized (buffer)
{
__default.appendMap(buffer,object);
DEFAULT.appendMap(buffer,object);
return buffer.toString();
}
}
/* ------------------------------------------------------------ */
public static String toString(Object[] array)
{
StringBuffer buffer=new StringBuffer(__default.getStringBufferSize());
StringBuffer buffer=new StringBuffer(DEFAULT.getStringBufferSize());
synchronized (buffer)
{
__default.appendArray(buffer,array);
DEFAULT.appendArray(buffer,array);
return buffer.toString();
}
}
/* ------------------------------------------------------------ */
/**
* @param s String containing JSON object or array.
* @return A Map, Object array or primitive array parsed from the JSON.
*/
public static Object parse(String s)
{
return __default.parse(new StringSource(s),false);
return DEFAULT.parse(new StringSource(s),false);
}
/* ------------------------------------------------------------ */
/**
* @param s String containing JSON object or array.
* @param stripOuterComment If true, an outer comment around the JSON is ignored.
@ -169,18 +172,20 @@ public class JSON
*/
public static Object parse(String s, boolean stripOuterComment)
{
return __default.parse(new StringSource(s),stripOuterComment);
return DEFAULT.parse(new StringSource(s),stripOuterComment);
}
/* ------------------------------------------------------------ */
/**
* @param in Reader containing JSON object or array.
* @return A Map, Object array or primitive array parsed from the JSON.
*/
public static Object parse(Reader in) throws IOException
{
return __default.parse(new ReaderSource(in),false);
return DEFAULT.parse(new ReaderSource(in),false);
}
/* ------------------------------------------------------------ */
/**
* @param s Stream containing JSON object or array.
* @param stripOuterComment If true, an outer comment around the JSON is ignored.
@ -188,9 +193,10 @@ public class JSON
*/
public static Object parse(Reader in, boolean stripOuterComment) throws IOException
{
return __default.parse(new ReaderSource(in),stripOuterComment);
return DEFAULT.parse(new ReaderSource(in),stripOuterComment);
}
/* ------------------------------------------------------------ */
/**
* @deprecated use {@link #parse(Reader)}
* @param in Reader containing JSON object or array.
@ -198,9 +204,10 @@ public class JSON
*/
public static Object parse(InputStream in) throws IOException
{
return __default.parse(new StringSource(IO.toString(in)),false);
return DEFAULT.parse(new StringSource(IO.toString(in)),false);
}
/* ------------------------------------------------------------ */
/**
* @deprecated use {@link #parse(Reader, boolean)}
* @param s Stream containing JSON object or array.
@ -209,7 +216,7 @@ public class JSON
*/
public static Object parse(InputStream in, boolean stripOuterComment) throws IOException
{
return __default.parse(new StringSource(IO.toString(in)),stripOuterComment);
return DEFAULT.parse(new StringSource(IO.toString(in)),stripOuterComment);
}
/* ------------------------------------------------------------ */
@ -237,7 +244,8 @@ public class JSON
Source source = new StringSource(json);
return parse(source);
}
/* ------------------------------------------------------------ */
/**
* Append object as JSON to string buffer.
* @param buffer
@ -273,11 +281,13 @@ public class JSON
}
}
/* ------------------------------------------------------------ */
public void appendNull(StringBuffer buffer)
{
buffer.append("null");
}
/* ------------------------------------------------------------ */
public void appendJSON(final StringBuffer buffer, final Convertor convertor, final Object object)
{
appendJSON(buffer,new Convertible()
@ -293,6 +303,7 @@ public class JSON
});
}
/* ------------------------------------------------------------ */
public void appendJSON(final StringBuffer buffer, Convertible converter)
{
final char[] c=
@ -368,11 +379,13 @@ public class JSON
buffer.append("}");
}
/* ------------------------------------------------------------ */
public void appendJSON(StringBuffer buffer, Generator generator)
{
generator.addJSON(buffer);
}
/* ------------------------------------------------------------ */
public void appendMap(StringBuffer buffer, Map object)
{
if (object==null)
@ -396,6 +409,7 @@ public class JSON
buffer.append('}');
}
/* ------------------------------------------------------------ */
public void appendArray(StringBuffer buffer, Collection collection)
{
if (collection==null)
@ -419,6 +433,7 @@ public class JSON
buffer.append(']');
}
/* ------------------------------------------------------------ */
public void appendArray(StringBuffer buffer, Object array)
{
if (array==null)
@ -440,6 +455,7 @@ public class JSON
buffer.append(']');
}
/* ------------------------------------------------------------ */
public void appendBoolean(StringBuffer buffer, Boolean b)
{
if (b==null)
@ -450,6 +466,7 @@ public class JSON
buffer.append(b.booleanValue()?"true":"false");
}
/* ------------------------------------------------------------ */
public void appendNumber(StringBuffer buffer, Number number)
{
if (number==null)
@ -460,6 +477,7 @@ public class JSON
buffer.append(number);
}
/* ------------------------------------------------------------ */
public void appendString(StringBuffer buffer, String string)
{
if (string==null)
@ -471,24 +489,21 @@ public class JSON
QuotedStringTokenizer.quote(buffer,string);
}
// Parsing utilities
/* ------------------------------------------------------------ */
protected String toString(char[] buffer,int offset,int length)
{
return new String(buffer,offset,length);
}
/* ------------------------------------------------------------ */
protected Map newMap()
{
return new HashMap();
}
/* ------------------------------------------------------------ */
protected Object[] newArray(int size)
{
return new Object[size];
@ -498,12 +513,14 @@ public class JSON
{
return this;
}
/* ------------------------------------------------------------ */
protected JSON contextFor(String field)
{
return this;
}
/* ------------------------------------------------------------ */
protected Object convertTo(Class type,Map map)
{
if (type!=null&&Convertible.class.isAssignableFrom(type))
@ -529,6 +546,7 @@ public class JSON
}
/* ------------------------------------------------------------ */
/**
* Register a {@link Convertor} for a class or interface.
* @param forClass The class or interface that the convertor applies to
@ -536,9 +554,10 @@ public class JSON
*/
public void addConvertor(Class forClass, Convertor convertor)
{
_convertors.put(forClass,convertor);
_convertors.put(forClass.getName(),convertor);
}
/* ------------------------------------------------------------ */
/**
* Lookup a convertor for a class.
* <p>
@ -549,28 +568,54 @@ public class JSON
*/
protected Convertor getConvertor(Class forClass)
{
Class c=forClass;
Convertor convertor=(Convertor)_convertors.get(c);
if (convertor==null && this!=__default)
convertor=__default.getConvertor(forClass);
Class cls=forClass;
Convertor convertor=(Convertor)_convertors.get(cls.getName());
if (convertor==null && this!=DEFAULT)
convertor=DEFAULT.getConvertor(cls);
while (convertor==null&&c!=null&&c!=Object.class)
while (convertor==null&&cls!=null&&cls!=Object.class)
{
Class[] ifs=c.getInterfaces();
Class[] ifs=cls.getInterfaces();
int i=0;
while (convertor==null&&ifs!=null&&i<ifs.length)
convertor=(Convertor)_convertors.get(ifs[i++]);
convertor=(Convertor)_convertors.get(ifs[i++].getName());
if (convertor==null)
{
c=c.getSuperclass();
convertor=(Convertor)_convertors.get(c);
cls=cls.getSuperclass();
convertor=(Convertor)_convertors.get(cls.getName());
}
}
return convertor;
}
/* ------------------------------------------------------------ */
/**
* Register a {@link Convertor} for a named class or interface.
* @param name name of a class or an interface that the convertor applies to
* @param convertor the convertor
*/
public void addConvertorFor(String name, Convertor convertor)
{
_convertors.put(name,convertor);
}
/* ------------------------------------------------------------ */
/**
* Lookup a convertor for a named class.
*
* @param name name of the class
* @return a {@link Convertor} or null if none were found.
*/
public Convertor getConvertorFor(String name)
{
String clsName=name;
Convertor convertor=(Convertor)_convertors.get(clsName);
if (convertor==null && this!=DEFAULT)
convertor=DEFAULT.getConvertorFor(clsName);
return convertor;
}
/* ------------------------------------------------------------ */
public Object parse(Source source, boolean stripOuterComment)
{
int comment_state=0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//"
@ -658,7 +703,8 @@ public class JSON
return o;
}
/* ------------------------------------------------------------ */
public Object parse(Source source)
{
int comment_state=0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//"
@ -754,12 +800,14 @@ public class JSON
return null;
}
/* ------------------------------------------------------------ */
protected Object handleUnknown(Source source, char c)
{
throw new IllegalStateException("unknown char '"+c+"'("+(int)c+") in "+source);
}
/* ------------------------------------------------------------ */
protected Object parseObject(Source source)
{
if (source.next()!='{')
@ -807,7 +855,7 @@ public class JSON
return map;
}
/* ------------------------------------------------------------ */
protected Object parseArray(Source source)
{
if (source.next()!='[')
@ -875,6 +923,7 @@ public class JSON
}
/* ------------------------------------------------------------ */
protected String parseString(Source source)
{
if (source.next()!='"')
@ -1025,6 +1074,7 @@ public class JSON
}
}
/* ------------------------------------------------------------ */
public Number parseNumber(Source source)
{
boolean minus=false;
@ -1111,6 +1161,7 @@ public class JSON
}
/* ------------------------------------------------------------ */
protected void seekTo(char seek, Source source)
{
while (source.hasNext())
@ -1127,6 +1178,7 @@ public class JSON
throw new IllegalStateException("Expected '"+seek+"'");
}
/* ------------------------------------------------------------ */
protected char seekTo(String seek, Source source)
{
while (source.hasNext())
@ -1145,6 +1197,7 @@ public class JSON
throw new IllegalStateException("Expected one of '"+seek+"'");
}
/* ------------------------------------------------------------ */
protected static void complete(String seek, Source source)
{
int i=0;
@ -1159,7 +1212,8 @@ public class JSON
throw new IllegalStateException("Expected \""+seek+"\"");
}
/* ------------------------------------------------------------ */
public interface Source
{
boolean hasNext();
@ -1171,6 +1225,7 @@ public class JSON
char[] scratchBuffer();
}
/* ------------------------------------------------------------ */
public static class StringSource implements Source
{
private final String string;
@ -1213,6 +1268,7 @@ public class JSON
}
}
/* ------------------------------------------------------------ */
public static class ReaderSource implements Source
{
private Reader _reader;

View File

@ -0,0 +1,85 @@
// ========================================================================
// Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.util.ajax;
import java.util.Map;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.ajax.JSON.Convertor;
import org.eclipse.jetty.util.ajax.JSON.Output;
public class JSONPojoConvertorFactory implements JSON.Convertor
{
private JSON _json=null;
public JSONPojoConvertorFactory(JSON json)
{
if (json==null)
{
throw new IllegalArgumentException();
}
_json=json;
}
public void toJSON(Object obj, Output out)
{
String clsName=obj.getClass().getName();
Convertor convertor=_json.getConvertorFor(clsName);
if (convertor==null)
{
try
{
Class cls=Loader.loadClass(JSON.class,clsName);
convertor=new JSONPojoConvertor(cls);
_json.addConvertorFor(clsName, convertor);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
if (convertor!=null)
{
convertor.toJSON(obj, out);
}
}
public Object fromJSON(Map object)
{
Map map=object;
String clsName=(String)map.get("class");
if (clsName!=null)
{
Convertor convertor=_json.getConvertorFor(clsName);
if (convertor==null)
{
try
{
Class cls=Loader.loadClass(JSON.class,clsName);
convertor=new JSONPojoConvertor(cls);
_json.addConvertorFor(clsName, convertor);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
if (convertor!=null)
{
return convertor.fromJSON(object);
}
}
return map;
}
}

View File

@ -0,0 +1,334 @@
// ========================================================================
// Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.util.ajax;
import junit.framework.TestCase;
/**
* Test to convert POJOs to JSON and vice versa with automatic convertor creation.
*/
public class JSONPojoConvertorFactoryTest extends TestCase {
public void testFoo()
{
JSON jsonOut = new JSON();
JSON jsonIn = new JSON();
jsonOut.registerConvertor(Object.class, new JSONPojoConvertorFactory(jsonOut));
jsonIn.registerConvertor(Object.class, new JSONPojoConvertorFactory(jsonIn));
Foo foo = new Foo();
foo._name = "Foo @ " + System.currentTimeMillis();
foo._int1 = 1;
foo._int2 = new Integer(2);
foo._long1 = 1000001l;
foo._long2 = new Long(1000002l);
foo._float1 = 10.11f;
foo._float2 = new Float(10.22f);
foo._double1 = 10000.11111d;
foo._double2 = new Double(10000.22222d);
Bar bar = new Bar("Hello", true, new Baz("World", Boolean.FALSE, foo), new Baz[]{
new Baz("baz0", Boolean.TRUE, null), new Baz("baz1", Boolean.FALSE, null)
});
String s = jsonOut.toJSON(bar);
Object obj = jsonIn.parse(new JSON.StringSource(s));
assertTrue(obj instanceof Bar);
Bar br = (Bar)obj;
Baz bz = br.getBaz();
Foo f = bz.getFoo();
assertEquals(f, foo);
assertTrue(br.getBazs().length==2);
assertEquals(br.getBazs()[0].getMessage(), "baz0");
assertEquals(br.getBazs()[1].getMessage(), "baz1");
}
public static class Bar
{
private String _title, _nullTest;
private Baz _baz;
private boolean _boolean1;
private Baz[] _bazs;
public Bar()
{
}
public Bar(String title, boolean boolean1, Baz baz)
{
setTitle(title);
setBoolean1(boolean1);
setBaz(baz);
}
public Bar(String title, boolean boolean1, Baz baz, Baz[] bazs)
{
this(title, boolean1, baz);
setBazs(bazs);
}
public String toString()
{
return new StringBuffer().append("\n=== ").append(getClass().getSimpleName()).append(" ===")
.append("\ntitle: ").append(getTitle())
.append("\nboolean1: ").append(isBoolean1())
.append("\nnullTest: ").append(getNullTest())
.append("\nbaz: ").append(getBaz()).toString();
}
public void setTitle(String title)
{
_title = title;
}
public String getTitle()
{
return _title;
}
public void setNullTest(String nullTest)
{
assert(nullTest==null);
_nullTest = nullTest;
}
public String getNullTest()
{
return _nullTest;
}
public void setBaz(Baz baz)
{
_baz = baz;
}
public Baz getBaz()
{
return _baz;
}
public void setBoolean1(boolean boolean1)
{
_boolean1 = boolean1;
}
public boolean isBoolean1()
{
return _boolean1;
}
public void setBazs(Baz[] bazs)
{
_bazs = bazs;
}
public Baz[] getBazs()
{
return _bazs;
}
}
public static class Baz
{
private String _message;
private Foo _foo;
private Boolean _boolean2;
public Baz()
{
}
public Baz(String message, Boolean boolean2, Foo foo)
{
setMessage(message);
setBoolean2(boolean2);
setFoo(foo);
}
public String toString()
{
return new StringBuffer().append("\n=== ").append(getClass().getSimpleName()).append(" ===")
.append("\nmessage: ").append(getMessage())
.append("\nboolean2: ").append(isBoolean2())
.append("\nfoo: ").append(getFoo()).toString();
}
public void setMessage(String message)
{
_message = message;
}
public String getMessage()
{
return _message;
}
public void setFoo(Foo foo)
{
_foo = foo;
}
public Foo getFoo()
{
return _foo;
}
public void setBoolean2(Boolean boolean2)
{
_boolean2 = boolean2;
}
public Boolean isBoolean2()
{
return _boolean2;
}
}
public static class Foo
{
private String _name;
private int _int1;
private Integer _int2;
private long _long1;
private Long _long2;
private float _float1;
private Float _float2;
private double _double1;
private Double _double2;
public Foo()
{
}
public String toString()
{
return new StringBuffer().append("\n=== ").append(getClass().getSimpleName()).append(" ===")
.append("\nname: ").append(_name)
.append("\nint1: ").append(_int1)
.append("\nint2: ").append(_int2)
.append("\nlong1: ").append(_long1)
.append("\nlong2: ").append(_long2)
.append("\nfloat1: ").append(_float1)
.append("\nfloat2: ").append(_float2)
.append("\ndouble1: ").append(_double1)
.append("\ndouble2: ").append(_double2)
.toString();
}
public boolean equals(Object another)
{
if(another instanceof Foo)
{
Foo foo = (Foo)another;
return getName().equals(foo.getName())
&& getInt1()==foo.getInt1()
&& getInt2().equals(foo.getInt2())
&& getLong1()==foo.getLong1()
&& getLong2().equals(foo.getLong2())
&& getFloat1()==foo.getFloat1()
&& getFloat2().equals(foo.getFloat2())
&& getDouble1()==foo.getDouble1()
&& getDouble2().equals(foo.getDouble2());
}
return false;
}
public String getName()
{
return _name;
}
public void setName(String name)
{
_name = name;
}
public int getInt1()
{
return _int1;
}
public void setInt1(int int1)
{
_int1 = int1;
}
public Integer getInt2()
{
return _int2;
}
public void setInt2(Integer int2)
{
_int2 = int2;
}
public long getLong1()
{
return _long1;
}
public void setLong1(long long1)
{
_long1 = long1;
}
public Long getLong2()
{
return _long2;
}
public void setLong2(Long long2)
{
_long2 = long2;
}
public float getFloat1()
{
return _float1;
}
public void setFloat1(float float1)
{
_float1 = float1;
}
public Float getFloat2()
{
return _float2;
}
public void setFloat2(Float float2)
{
_float2 = float2;
}
public double getDouble1()
{
return _double1;
}
public void setDouble1(double double1)
{
_double1 = double1;
}
public Double getDouble2()
{
return _double2;
}
public void setDouble2(Double double2)
{
_double2 = double2;
}
}
}