Compare commits
No commits in common. "3a756a1dade91309d6632b41a74db4deaa89af19" and "0763ad1fd67fde064c71b2ec79f566924f0a8239" have entirely different histories.
3a756a1dad
...
0763ad1fd6
64 changed files with 233 additions and 1882 deletions
|
|
@ -1,4 +0,0 @@
|
||||||
# JsonToy-2
|
|
||||||
my second (or third) go at writing a java json reader/writer
|
|
||||||
|
|
||||||
Toy project, aiming for a low-latency tool, optimizing on memory usage
|
|
||||||
8
pom.xml
8
pom.xml
|
|
@ -59,9 +59,9 @@
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>5.6.2</version>
|
<version>4.13</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<version>2.10.5.1</version>
|
<version>2.10.3</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
package nl.sander.jsontoy2;
|
|
||||||
|
|
||||||
import javassist.*;
|
|
||||||
import nl.sander.jsontoy2.javassist.ClassCreationException;
|
|
||||||
import nl.sander.jsontoy2.javassist.Javassist;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responsible for generating classes that parse into java beans.
|
|
||||||
* <p>
|
|
||||||
* TODO remove javassist both for analysing java beans and for generating bytecode
|
|
||||||
*/
|
|
||||||
public class JavaObjectReaderFactory {
|
|
||||||
public static final String ROOT_PACKAGE = "serializer.";
|
|
||||||
private final static CtClass DESERIALIZER_BASE = Javassist.getTypeDefinition(JsonValueReader.class);
|
|
||||||
private static final CtClass[] NO_EXCEPTIONS = {};
|
|
||||||
private static final CtClass[] PARSER_PARAM = {Javassist.getTypeDefinition(Parser.class)};
|
|
||||||
private static final Class<?>[] NO_PARAMS = {};
|
|
||||||
private static final CtClass[] CT_NO_PARAMS = {};
|
|
||||||
private final static CtClass OBJECT_CLASS = Javassist.getTypeDefinition(Object.class);
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
static <T> JsonValueReader<T> createReaderInstance(Class<T> type) {
|
|
||||||
try {
|
|
||||||
Class<?> jsonTypeReader = createReaderClass(type);
|
|
||||||
return (JsonValueReader<T>) jsonTypeReader.getDeclaredConstructor(NO_PARAMS).newInstance(new Object[]{});
|
|
||||||
|
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
|
||||||
throw new JsonParseException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Class<?> createReaderClass(Class<?> type) {
|
|
||||||
String name = createDeserializerName(type);
|
|
||||||
CtClass deserializerClass = Javassist.createClass(name, DESERIALIZER_BASE);
|
|
||||||
|
|
||||||
try {
|
|
||||||
deserializerClass.addConstructor(CtNewConstructor.make(CT_NO_PARAMS, NO_EXCEPTIONS, "{super();}", deserializerClass));
|
|
||||||
deserializerClass.addMethod(createReadJsonMethod(deserializerClass, type));
|
|
||||||
return deserializerClass.toClass();
|
|
||||||
} catch (CannotCompileException e) {
|
|
||||||
throw new ClassCreationException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static CtMethod createReadJsonMethod(CtClass serializerClass, Class<?> type) {
|
|
||||||
try {
|
|
||||||
String readMethodBodySource = createReadMethodBodySource(type);
|
|
||||||
// System.out.println(readMethodBodySource);
|
|
||||||
return CtNewMethod.make(Modifier.PUBLIC, OBJECT_CLASS, "read", PARSER_PARAM, NO_EXCEPTIONS, readMethodBodySource, serializerClass);
|
|
||||||
} catch (CannotCompileException e) {
|
|
||||||
throw new ClassCreationException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static String createReadMethodBodySource(Class<?> type) {
|
|
||||||
String source = "{";
|
|
||||||
String typeName = type.getName();
|
|
||||||
if (ReaderFactory.readerSuppliers.containsKey(type)) {
|
|
||||||
source += "return " + JsonReader.class.getName() + ".read(" + typeName + ".class, $1);";
|
|
||||||
} else {
|
|
||||||
source += "java.util.Map object=" + JsonReader.class.getName() + ".readJavaObject(" + typeName + ".class,$1);\n";
|
|
||||||
source += typeName + " instance = new " + typeName + "();\n";
|
|
||||||
|
|
||||||
for (Field field : type.getDeclaredFields()) {
|
|
||||||
source += "instance.set" + capitalize(field.getName())
|
|
||||||
+ "(" + getSourceForGetValueFromObject(field) + ");\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
source += "return instance;";
|
|
||||||
}
|
|
||||||
|
|
||||||
source += "}\n";
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static String getSourceForGetValueFromObject(Field field) {
|
|
||||||
Class<?> fieldType = field.getType();
|
|
||||||
String fieldName = field.getName();
|
|
||||||
if (fieldType == boolean.class) {
|
|
||||||
return "getBoolean(\"" + fieldName + "\", object)";
|
|
||||||
} else if (fieldType == int.class) {
|
|
||||||
return "getInt(\"" + fieldName + "\", object)";
|
|
||||||
} else if (fieldType == long.class) {
|
|
||||||
return "getLong(\"" + fieldName + "\",object)";
|
|
||||||
} else if (fieldType == short.class) {
|
|
||||||
return "getShort(\"" + fieldName + "\",object)";
|
|
||||||
} else if (fieldType == byte.class) {
|
|
||||||
return "getByte(\"" + fieldName + "\",object)";
|
|
||||||
} else if (fieldType == float.class) {
|
|
||||||
return "getFloat(\"" + fieldName + "\",object)";
|
|
||||||
} else if (fieldType == double.class) {
|
|
||||||
return "getDouble(\"" + fieldName + "\",object)";
|
|
||||||
} else {
|
|
||||||
String fieldTypeName = field.getType().getName();
|
|
||||||
if (Set.class.isAssignableFrom(fieldType)) {
|
|
||||||
return "(" + fieldTypeName + ")getSet(\"" + fieldName + "\"," + fieldTypeName + ".class,object)";
|
|
||||||
} else if (List.class.isAssignableFrom(fieldType)) {
|
|
||||||
return "(" + fieldTypeName + ")getList(\"" + fieldName + "\"," + fieldTypeName + ".class,object)";
|
|
||||||
} else if (fieldType.isArray()) {
|
|
||||||
return "(" + format(fieldTypeName) + "[])getArray(\"" + fieldName + "\"," + format(fieldTypeName) + ".class,object)";
|
|
||||||
} else {
|
|
||||||
return "(" + fieldTypeName + ")(object.get(\"" + fieldName + "\"))";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String format(String arrayTypeExpr) {
|
|
||||||
return arrayTypeExpr.substring(2).substring(0, arrayTypeExpr.length() - 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
//should be reinstated
|
|
||||||
private static String genericType(CtField field) {
|
|
||||||
try {
|
|
||||||
if (!Javassist.isCollection(field.getType())) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
|
|
||||||
String genericSignature = field.getGenericSignature(); // java.util.List<java.lang.String;>;
|
|
||||||
Pattern p = Pattern.compile("(.+?)<(.+?);>;");
|
|
||||||
Matcher matcher = p.matcher(genericSignature);
|
|
||||||
if (matcher.find()) {
|
|
||||||
String[] genericTypes = matcher.group(2).substring(1).replaceAll("/", ".").split(";L");
|
|
||||||
for (int i = 0; i < genericTypes.length; i++) {
|
|
||||||
genericTypes[i] += ".class";
|
|
||||||
}
|
|
||||||
return String.join(",", genericTypes);
|
|
||||||
}
|
|
||||||
throw new ClassCreationException("Generic type not ok");
|
|
||||||
}
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
throw new ClassCreationException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String capitalize(String lowercase) {
|
|
||||||
return lowercase.substring(0, 1).toUpperCase() + lowercase.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* custom root package is prepended to avoid the java.lang class in which it's illegal to create new classes
|
|
||||||
*
|
|
||||||
* Array marks ( '[]' ) are replaced by the 'Array', Otherwise the SerializerClassName would be syntactically incorrect
|
|
||||||
*/
|
|
||||||
private static String createDeserializerName(Class<?> type) {
|
|
||||||
return ROOT_PACKAGE + type.getName().replaceAll("\\[]", "Array") + "Deserializer";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +1,32 @@
|
||||||
package nl.sander.jsontoy2;
|
package nl.sander.jsontoy2;
|
||||||
|
|
||||||
|
import nl.sander.jsontoy2.readers.*;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* public api
|
* public api
|
||||||
*/
|
*/
|
||||||
public class JsonReader {
|
public class JsonReader {
|
||||||
|
private static final ConcurrentMap<Class<?>, Supplier<JsonValueReader<?>>> readSuppliers = new ConcurrentHashMap<>();
|
||||||
|
private static final ConcurrentMap<Class<?>, JsonValueReader<?>> readers = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private JsonReader() {
|
private final static ThreadLocal<SoftReference<Parser>> PARSERS = new ThreadLocal<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
registerPrimitiveTypeReaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -26,12 +43,21 @@ public class JsonReader {
|
||||||
* array => List
|
* array => List
|
||||||
*/
|
*/
|
||||||
public static Object read(InputStream inputStream) {
|
public static Object read(InputStream inputStream) {
|
||||||
final InputStream in = ensureBufferedStream(inputStream);
|
InputStream in = ensureBuffered(inputStream);
|
||||||
try (Parser parser = ParserFactory.getParser(in)) {
|
try (Parser parser = getParser(in)) {
|
||||||
return read(parser);
|
return read(parser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static InputStream ensureBuffered(InputStream inputStream) {
|
||||||
|
if (inputStream instanceof BufferedInputStream) {
|
||||||
|
return inputStream;
|
||||||
|
} else {
|
||||||
|
return new BufferedInputStream(inputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a value from a string for a type that is not known beforehand
|
* Reads a value from a string for a type that is not known beforehand
|
||||||
*
|
*
|
||||||
|
|
@ -39,7 +65,29 @@ public class JsonReader {
|
||||||
* @return @see read(InputStream stream)
|
* @return @see read(InputStream stream)
|
||||||
*/
|
*/
|
||||||
public static Object read(String jsonString) {
|
public static Object read(String jsonString) {
|
||||||
return read(ParserFactory.getParser(jsonString));
|
return read(getParser(jsonString));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Parser getParser(String jsonString) {
|
||||||
|
return getParser(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Parser getParser(InputStream inputStream) {
|
||||||
|
Objects.requireNonNull(inputStream, "File not found");
|
||||||
|
Parser parser;
|
||||||
|
SoftReference<Parser> parserReference = PARSERS.get();
|
||||||
|
if (parserReference == null || (parser = parserReference.get()) == null) {
|
||||||
|
parser = new Parser(inputStream);
|
||||||
|
parserReference = new SoftReference<>(parser);
|
||||||
|
PARSERS.set(parserReference);
|
||||||
|
} else {
|
||||||
|
parser.init(inputStream);
|
||||||
|
}
|
||||||
|
return parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object read(Parser parser) {
|
||||||
|
return parser.parseAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -51,7 +99,7 @@ public class JsonReader {
|
||||||
* @return Object the specified type
|
* @return Object the specified type
|
||||||
*/
|
*/
|
||||||
public static <T> T read(Class<T> type, InputStream inputStream) {
|
public static <T> T read(Class<T> type, InputStream inputStream) {
|
||||||
Parser parser = ParserFactory.getParser(inputStream);
|
Parser parser = getParser(inputStream);
|
||||||
T value = read(type, parser);
|
T value = read(type, parser);
|
||||||
parser.close();
|
parser.close();
|
||||||
return value;
|
return value;
|
||||||
|
|
@ -66,34 +114,44 @@ public class JsonReader {
|
||||||
* @return Object the specified type
|
* @return Object the specified type
|
||||||
*/
|
*/
|
||||||
public static <T> T read(Class<T> type, String jsonString) {
|
public static <T> T read(Class<T> type, String jsonString) {
|
||||||
return read(type, ParserFactory.getParser(jsonString));
|
return read(type, getParser(jsonString));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Object read(Parser parser) {
|
|
||||||
return parser.parseAny();
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO should not be public
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T> T read(Class<T> type, Parser parser) {
|
private static <T> T read(Class<T> type, Parser parser) {
|
||||||
return (T) ReaderFactory.getReader(type).read(parser);
|
return (T) getReader(type).read(parser);
|
||||||
// class.cast() does not work well for primitives;
|
// class.cast() does not work well for primitives;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
private static <T> JsonValueReader<?> getReader(Class<T> type) {
|
||||||
public static Map<?, ?> readJavaObject(Class<?> type, Parser parser) {
|
return readers.computeIfAbsent(type, k -> readSuppliers.get(k).get());
|
||||||
return parser.parseObject(type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> void register(Class<T> type, Supplier<JsonValueReader<?>> objectReader) {
|
||||||
private static InputStream ensureBufferedStream(InputStream inputStream) {
|
readSuppliers.put(type, objectReader);
|
||||||
if (inputStream instanceof BufferedInputStream) {
|
|
||||||
return inputStream;
|
|
||||||
} else {
|
|
||||||
return new BufferedInputStream(inputStream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void registerPrimitiveTypeReaders() {
|
||||||
|
register(Boolean.class, BooleanReader::new);
|
||||||
|
register(boolean.class, BooleanReader::new);
|
||||||
|
register(Integer.class, IntegerReader::new);
|
||||||
|
register(int.class, IntegerReader::new);
|
||||||
|
register(Long.class, LongReader::new);
|
||||||
|
register(long.class, LongReader::new);
|
||||||
|
register(Byte.class, ByteReader::new);
|
||||||
|
register(byte.class, ByteReader::new);
|
||||||
|
register(Short.class, ShortReader::new);
|
||||||
|
register(short.class, ShortReader::new);
|
||||||
|
register(Double.class, DoubleReader::new);
|
||||||
|
register(double.class, DoubleReader::new);
|
||||||
|
register(Float.class, FloatReader::new);
|
||||||
|
register(float.class, FloatReader::new);
|
||||||
|
register(Date.class, DateReader::new);
|
||||||
|
register(Character.class, CharReader::new);
|
||||||
|
register(char.class, CharReader::new);
|
||||||
|
register(String.class, StringReader::new);
|
||||||
|
register(LocalDateTime.class, LocalDateTimeReader::new);
|
||||||
|
register(List.class, ListReader::new);
|
||||||
|
register(Map.class, MapReader::new);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,112 +1,5 @@
|
||||||
package nl.sander.jsontoy2;
|
package nl.sander.jsontoy2;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
public interface JsonValueReader<T> {
|
||||||
import java.lang.reflect.Constructor;
|
T read(Parser parser);
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for generated readers
|
|
||||||
*
|
|
||||||
* @param <T> the type that the reader produces
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused") /* these methods will be called from generated code*/
|
|
||||||
public abstract class JsonValueReader<T> {
|
|
||||||
public abstract T read(Parser parser);
|
|
||||||
|
|
||||||
protected boolean getBoolean(String fieldName, Map<String, ?> values) {
|
|
||||||
Object value = values.get(fieldName);
|
|
||||||
return value == null ? false : (Boolean) value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getInt(String fieldName, Map<String, ?> values) {
|
|
||||||
Object value = values.get(fieldName);
|
|
||||||
return value == null ? 0 : (Integer) value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected long getLong(String fieldName, Map<String, ?> values) {
|
|
||||||
Object value = values.get(fieldName);
|
|
||||||
return value == null ? 0L : (Long) value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected short getShort(String fieldName, Map<String, ?> values) {
|
|
||||||
Object value = values.get(fieldName);
|
|
||||||
return value == null ? (short) 0 : (Short) value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected byte getByte(String fieldName, Map<String, ?> values) {
|
|
||||||
Object value = values.get(fieldName);
|
|
||||||
return value == null ? (byte) 0 : (Byte) value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected float getFloat(String fieldName, Map<String, ?> values) {
|
|
||||||
Object value = values.get(fieldName);
|
|
||||||
return value == null ? 0F : (Float) value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected double getDouble(String fieldName, Map<String, ?> values) {
|
|
||||||
Object value = values.get(fieldName);
|
|
||||||
return value == null ? 0D : (Double) value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Creates a Set type for any implementation of it (that the containing bean needs),
|
|
||||||
* unless it has no constructor using a java.util.Collection as single parameter.
|
|
||||||
*
|
|
||||||
* returntype is generic Set, object needs cast afterwards in generated code
|
|
||||||
*/
|
|
||||||
protected Set<?> getSet(String fieldName, Class<? extends Set<?>> type, Map<String, ?> values) {
|
|
||||||
List<?> value = (List<?>) values.get(fieldName);
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
if (type.equals(Set.class)) {
|
|
||||||
return new HashSet<>(value);
|
|
||||||
} else {
|
|
||||||
Constructor<? extends Set<?>> constructor = type.getConstructor(Collection.class);
|
|
||||||
return constructor.newInstance(value);
|
|
||||||
}
|
|
||||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new JsonParseException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Creates a List type for any implementation of it (that the containing bean needs),
|
|
||||||
* unless it has no constructor using a java.util.Collection as single parameter.
|
|
||||||
*
|
|
||||||
* returntype is generic List, needs cast afterwards in generated code
|
|
||||||
*/
|
|
||||||
protected List<?> getList(String fieldName, Class<?> listImplType, Map<String, ?> values) {
|
|
||||||
List<?> value = (List<?>) values.get(fieldName);
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
} else if (listImplType == ArrayList.class || listImplType.equals(List.class)) {
|
|
||||||
return value;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
Constructor<?> constructor = listImplType.getConstructor(Collection.class);
|
|
||||||
return (List<?>) constructor.newInstance(value);
|
|
||||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new JsonParseException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object getArray(String fieldName, Class<?> arrayType, Map<String, ?> values) {
|
|
||||||
List<?> value = (List<?>) values.get(fieldName);
|
|
||||||
if (value == null) {
|
|
||||||
return new Object[]{};
|
|
||||||
} else {
|
|
||||||
Object[] array = (Object[]) Array.newInstance(arrayType, value.size());
|
|
||||||
int index = 0;
|
|
||||||
for (Object element : value) {
|
|
||||||
array[index] = value.get(index++);
|
|
||||||
}
|
|
||||||
//TODO this can probably be done smarter
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,6 @@ public class Lexer implements AutoCloseable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void eatUntil(char until) {
|
|
||||||
eatUntil(new char[]{until});
|
|
||||||
}
|
|
||||||
|
|
||||||
void eatUntil(char... until) {
|
void eatUntil(char... until) {
|
||||||
while (current > -1 && (!contains(until, current) | Character.isWhitespace(current))) {
|
while (current > -1 && (!contains(until, current) | Character.isWhitespace(current))) {
|
||||||
advance();
|
advance();
|
||||||
|
|
@ -71,9 +67,4 @@ public class Lexer implements AutoCloseable {
|
||||||
throw new JsonParseException(e);
|
throw new JsonParseException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public byte current() {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,37 +37,37 @@ public class Parser extends Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer parseInteger() {
|
public Integer parseInteger() {
|
||||||
final String value = parseNumber();
|
String value = parseNumber();
|
||||||
return Double.valueOf(value).intValue();
|
return Double.valueOf(value).intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long parseLong() {
|
public Long parseLong() {
|
||||||
final String value = parseNumber();
|
String value = parseNumber();
|
||||||
return Long.parseLong(value);
|
return Long.parseLong(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Float parseFloat() {
|
public Float parseFloat() {
|
||||||
final String value = parseNumber();
|
String value = parseNumber();
|
||||||
return Float.parseFloat(value);
|
return Float.parseFloat(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double parseDouble() {
|
public Double parseDouble() {
|
||||||
final String value = parseNumber();
|
String value = parseNumber();
|
||||||
return Double.parseDouble(value);
|
return Double.parseDouble(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Short parseShort() {
|
public Short parseShort() {
|
||||||
final String value = parseNumber();
|
String value = parseNumber();
|
||||||
return Short.parseShort(value);
|
return Short.parseShort(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Byte parseByte() {
|
public Byte parseByte() {
|
||||||
final String value = parseNumber();
|
String value = parseNumber();
|
||||||
return Byte.parseByte(value);
|
return Byte.parseByte(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Character parseCharacter() {
|
public Character parseCharacter() {
|
||||||
final String string = parseString();
|
String string = parseString();
|
||||||
return string.charAt(0);
|
return string.charAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +78,7 @@ public class Parser extends Lexer {
|
||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
final String maybeBoolean = characterBuffer.toString();
|
String maybeBoolean = characterBuffer.toString();
|
||||||
boolean returnValue;
|
boolean returnValue;
|
||||||
if ((returnValue = maybeBoolean.equals("true")) || maybeBoolean.equals("false")) {
|
if ((returnValue = maybeBoolean.equals("true")) || maybeBoolean.equals("false")) {
|
||||||
return returnValue;
|
return returnValue;
|
||||||
|
|
@ -105,18 +105,14 @@ public class Parser extends Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<?> parseArray() {
|
public List<?> parseArray() {
|
||||||
return parseArray(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<?> parseArray(Class<?> genericType) {
|
|
||||||
skipWhitespace();
|
skipWhitespace();
|
||||||
if (current != '[') {
|
if (current != '[') {
|
||||||
throw new JsonParseException("no list found");
|
throw new JsonParseException("no list found");
|
||||||
}
|
}
|
||||||
final List<Object> list = new ArrayList<>();
|
List<Object> list = new ArrayList<>();
|
||||||
advance();
|
advance();
|
||||||
while (current != -1 && current != ']') {
|
while (current != -1 && current != ']') {
|
||||||
final Maybe<Object> maybeValue = genericType == null ? parseValue() : parseValue(genericType);
|
Maybe<Object> maybeValue = parseValue();
|
||||||
if (!maybeValue.isPresent()) {
|
if (!maybeValue.isPresent()) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -129,11 +125,7 @@ public class Parser extends Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<?, ?> parseObject() {
|
public Map<?, ?> parseObject() {
|
||||||
return parseObject(null);
|
HashMap<Object, Object> map = new HashMap<>();
|
||||||
}
|
|
||||||
|
|
||||||
public Map<?, ?> parseObject(Class<?> type) {
|
|
||||||
final HashMap<Object, Object> map = new HashMap<>();
|
|
||||||
skipWhitespace();
|
skipWhitespace();
|
||||||
if (current != '{') {
|
if (current != '{') {
|
||||||
throw new JsonParseException("no map found");
|
throw new JsonParseException("no map found");
|
||||||
|
|
@ -142,7 +134,7 @@ public class Parser extends Lexer {
|
||||||
while (current != -1 && current != '}') {
|
while (current != -1 && current != '}') {
|
||||||
skipWhitespace();
|
skipWhitespace();
|
||||||
if (current == '"') {
|
if (current == '"') {
|
||||||
final String key = parseString();
|
String key = parseString();
|
||||||
skipWhitespace();
|
skipWhitespace();
|
||||||
if (current == ':') {
|
if (current == ':') {
|
||||||
advance();
|
advance();
|
||||||
|
|
@ -150,13 +142,8 @@ public class Parser extends Lexer {
|
||||||
throw new JsonParseException("expected colon");
|
throw new JsonParseException("expected colon");
|
||||||
}
|
}
|
||||||
skipWhitespace();
|
skipWhitespace();
|
||||||
final Maybe<Object> maybeValue;
|
Maybe<Object> maybeValue = parseValue();
|
||||||
try {
|
maybeValue.ifPresent(value -> map.put(key, value));
|
||||||
maybeValue = type == null ? parseValue() : Maybe.of(JsonReader.read(type.getDeclaredField(key).getType(), this));
|
|
||||||
maybeValue.ifPresent(value -> map.put(key, value));
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
throw new JsonParseException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
advance();
|
advance();
|
||||||
skipWhitespace();
|
skipWhitespace();
|
||||||
|
|
@ -165,7 +152,7 @@ public class Parser extends Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object parseAny() {
|
public Object parseAny() {
|
||||||
final Maybe<Object> maybe = parseValue();
|
Maybe<Object> maybe = parseValue();
|
||||||
if (maybe.isPresent()) {
|
if (maybe.isPresent()) {
|
||||||
return maybe.get();
|
return maybe.get();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -174,7 +161,7 @@ public class Parser extends Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Maybe<Object> parseValue() {
|
private Maybe<Object> parseValue() {
|
||||||
final Object value;
|
Object value;
|
||||||
skipWhitespace();
|
skipWhitespace();
|
||||||
switch (current) {
|
switch (current) {
|
||||||
case ']':
|
case ']':
|
||||||
|
|
@ -194,8 +181,8 @@ public class Parser extends Lexer {
|
||||||
break;
|
break;
|
||||||
default: String numeric = parseNumber();
|
default: String numeric = parseNumber();
|
||||||
double doubleValue = Double.parseDouble(numeric);
|
double doubleValue = Double.parseDouble(numeric);
|
||||||
if ((long) doubleValue == doubleValue) {
|
if ((int) doubleValue == doubleValue) {
|
||||||
value = (long) doubleValue;
|
value = (int) doubleValue;
|
||||||
} else {
|
} else {
|
||||||
value = doubleValue;
|
value = doubleValue;
|
||||||
}
|
}
|
||||||
|
|
@ -203,20 +190,6 @@ public class Parser extends Lexer {
|
||||||
return Maybe.of(value);
|
return Maybe.of(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Maybe<Object> parseValue(Class<?> type) {
|
|
||||||
final Object value;
|
|
||||||
skipWhitespace();
|
|
||||||
|
|
||||||
JsonValueReader<?> reader = ReaderFactory.getReader(type);
|
|
||||||
if (reader != null) {
|
|
||||||
value = reader.read(this);
|
|
||||||
} else {
|
|
||||||
value = parseObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Maybe.of(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object readNull() {
|
private Object readNull() {
|
||||||
expect(() -> new JsonParseException("Expected 'null', encountered " + (char) current), 'n', 'u', 'l', 'l');
|
expect(() -> new JsonParseException("Expected 'null', encountered " + (char) current), 'n', 'u', 'l', 'l');
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -278,8 +251,8 @@ public class Parser extends Lexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseEncoded() {
|
private void parseEncoded() {
|
||||||
final StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
final char codePoint = parseCodePoint();
|
char codePoint = parseCodePoint();
|
||||||
buf.append(codePoint);
|
buf.append(codePoint);
|
||||||
if (Character.isHighSurrogate(codePoint)) {
|
if (Character.isHighSurrogate(codePoint)) {
|
||||||
expect(() -> new JsonParseException("Invalid unicode codepoint at line " + linecount), '\\', 'u');
|
expect(() -> new JsonParseException("Invalid unicode codepoint at line " + linecount), '\\', 'u');
|
||||||
|
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
package nl.sander.jsontoy2;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.lang.ref.SoftReference;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps a Threadlocal parser that can be (re)used by clients.
|
|
||||||
* <p>
|
|
||||||
* The parser is not kept indefinitely, but garbage collected when the JVM considers necessary.
|
|
||||||
*/
|
|
||||||
public class ParserFactory {
|
|
||||||
|
|
||||||
private final static ThreadLocal<SoftReference<Parser>> PARSERS = new ThreadLocal<>();
|
|
||||||
|
|
||||||
private ParserFactory() {
|
|
||||||
}
|
|
||||||
|
|
||||||
static Parser getParser(String jsonString) {
|
|
||||||
return getParser(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static Parser getParser(InputStream inputStream) {
|
|
||||||
Objects.requireNonNull(inputStream, "File not found");
|
|
||||||
Parser parser;
|
|
||||||
SoftReference<Parser> parserReference = PARSERS.get();
|
|
||||||
if (parserReference == null || (parser = parserReference.get()) == null) {
|
|
||||||
parser = new Parser(inputStream);
|
|
||||||
parserReference = new SoftReference<>(parser);
|
|
||||||
PARSERS.set(parserReference);
|
|
||||||
} else {
|
|
||||||
parser.init(inputStream);
|
|
||||||
}
|
|
||||||
return parser;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
package nl.sander.jsontoy2;
|
|
||||||
|
|
||||||
import nl.sander.jsontoy2.readers.*;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ReaderFactory {
|
|
||||||
static final ConcurrentMap<Class<?>, Supplier<JsonValueReader<?>>> readerSuppliers = new ConcurrentHashMap<>();
|
|
||||||
private static final ConcurrentMap<Class<?>, JsonValueReader<?>> readers = new ConcurrentHashMap<>();
|
|
||||||
private final static MapReader MAPREADER = new MapReader();
|
|
||||||
private final static ListReader LISTREADER = new ListReader();
|
|
||||||
|
|
||||||
static {
|
|
||||||
registerPrimitiveTypeReaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ReaderFactory() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> void registerCustomReader(Class<T> type, JsonValueReader<T> reader) {
|
|
||||||
readers.put(type, reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> void register(Class<T> type, Supplier<JsonValueReader<?>> objectReader) {
|
|
||||||
readerSuppliers.put(type, objectReader);
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> JsonValueReader<?> getReader(Class<T> type) {
|
|
||||||
if (Map.class.isAssignableFrom(type)) {
|
|
||||||
return MAPREADER;
|
|
||||||
} else if (List.class.isAssignableFrom(type) || Set.class.isAssignableFrom(type) || type.isArray()) {
|
|
||||||
return LISTREADER;
|
|
||||||
} else {
|
|
||||||
return readers.computeIfAbsent(type, k -> {
|
|
||||||
Supplier<JsonValueReader<?>> jsonValueReaderSupplier = readerSuppliers.get(k);
|
|
||||||
|
|
||||||
return Optional.ofNullable(jsonValueReaderSupplier)
|
|
||||||
.orElseGet(() -> () -> JavaObjectReaderFactory.createReaderInstance(type))
|
|
||||||
.get();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void registerPrimitiveTypeReaders() {
|
|
||||||
register(Boolean.class, BooleanReader::new);
|
|
||||||
register(boolean.class, BooleanReader::new);
|
|
||||||
register(Integer.class, IntegerReader::new);
|
|
||||||
register(int.class, IntegerReader::new);
|
|
||||||
register(Long.class, LongReader::new);
|
|
||||||
register(long.class, LongReader::new);
|
|
||||||
register(Byte.class, ByteReader::new);
|
|
||||||
register(byte.class, ByteReader::new);
|
|
||||||
register(Short.class, ShortReader::new);
|
|
||||||
register(short.class, ShortReader::new);
|
|
||||||
register(Double.class, DoubleReader::new);
|
|
||||||
register(double.class, DoubleReader::new);
|
|
||||||
register(Float.class, FloatReader::new);
|
|
||||||
register(float.class, FloatReader::new);
|
|
||||||
register(Date.class, DateReader::new);
|
|
||||||
register(Character.class, CharReader::new);
|
|
||||||
register(char.class, CharReader::new);
|
|
||||||
register(String.class, StringReader::new);
|
|
||||||
register(LocalDateTime.class, LocalDateTimeReader::new);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,28 +2,17 @@ package nl.sander.jsontoy2.java;
|
||||||
|
|
||||||
import nl.sander.jsontoy2.java.constantpool.ConstantPoolEntry;
|
import nl.sander.jsontoy2.java.constantpool.ConstantPoolEntry;
|
||||||
import nl.sander.jsontoy2.java.constantpool.Utf8Entry;
|
import nl.sander.jsontoy2.java.constantpool.Utf8Entry;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains info from parsed bytecode
|
|
||||||
*/
|
|
||||||
public class ClassObject {
|
|
||||||
|
|
||||||
private final ConstantPoolEntry[] constantPool;
|
public class ClassObject<T> {
|
||||||
private final Info[] fieldInfos;
|
|
||||||
private final Info[] methodInfos;
|
|
||||||
|
|
||||||
private ClassObject(ConstantPoolEntry[] constantPool, Info[] fieldInfos, Info[] methodInfos) {
|
private ConstantPoolEntry[] constantPool;
|
||||||
this.constantPool = constantPool;
|
private Info[] fieldInfos;
|
||||||
this.fieldInfos = fieldInfos;
|
private Info[] methodInfos;
|
||||||
this.methodInfos = methodInfos;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUtf8(int index) {
|
private String getUtf8(int index) {
|
||||||
return ((Utf8Entry) constantPool[index - 1]).getUtf8();
|
return ((Utf8Entry) constantPool[index - 1]).getUtf8();
|
||||||
|
|
@ -37,50 +26,10 @@ public class ClassObject {
|
||||||
|
|
||||||
public Set<Method> getMethods() {
|
public Set<Method> getMethods() {
|
||||||
return Arrays.stream(methodInfos)
|
return Arrays.stream(methodInfos)
|
||||||
.map(this::createMethod)
|
.map(mi -> new Method(getUtf8(mi.getNameIndex()), getUtf8(mi.getDescriptorIndex())))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Method createMethod(Info mi) {
|
|
||||||
final String descriptor = getUtf8(mi.getDescriptorIndex());
|
|
||||||
final int split = descriptor.indexOf(')');
|
|
||||||
final List<String> parameters = getParameterTypes(descriptor.substring(0, split));
|
|
||||||
|
|
||||||
String returnType = descriptor.substring(split + 1);
|
|
||||||
returnType = returnType.substring(0, returnType.length() - 1);
|
|
||||||
return new Method(getUtf8(mi.getNameIndex()), parameters, returnType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getParameterTypes(String parameterDescriptor) {
|
|
||||||
List<String> result = new ArrayList<>();
|
|
||||||
boolean array = false;
|
|
||||||
for (int i = 1; i < parameterDescriptor.length(); i++) {
|
|
||||||
if (parameterDescriptor.charAt(i) == '[') {
|
|
||||||
array = true;
|
|
||||||
} else {
|
|
||||||
if (parameterDescriptor.charAt(i) == 'L') {
|
|
||||||
int i2 = i;
|
|
||||||
while (i2 < parameterDescriptor.length() && parameterDescriptor.charAt(i2) != ';') {
|
|
||||||
i2++;
|
|
||||||
}
|
|
||||||
result.add(getArrayIndicator(array) + parameterDescriptor.substring(i, i2));
|
|
||||||
array = false;
|
|
||||||
i = i2;
|
|
||||||
} else {
|
|
||||||
result.add(getArrayIndicator(array) + parameterDescriptor.charAt(i));
|
|
||||||
array = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private String getArrayIndicator(boolean array) {
|
|
||||||
return array ? "[" : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
for (ConstantPoolEntry entry : constantPool) {
|
for (ConstantPoolEntry entry : constantPool) {
|
||||||
|
|
@ -92,44 +41,41 @@ public class ClassObject {
|
||||||
|
|
||||||
public static class Builder<T> {
|
public static class Builder<T> {
|
||||||
|
|
||||||
|
private final ClassObject<T> classObject = new ClassObject<>();
|
||||||
private int constantPoolIndex = 0;
|
private int constantPoolIndex = 0;
|
||||||
private int fieldInfoIndex = 0;
|
private int fieldInfoIndex = 0;
|
||||||
private int methodInfoIndex = 0;
|
private int methodInfoIndex = 0;
|
||||||
|
|
||||||
private ConstantPoolEntry[] constantPool;
|
public ClassObject<T> build() {
|
||||||
private Info[] fieldInfos;
|
return classObject;
|
||||||
private Info[] methodInfos;
|
|
||||||
|
|
||||||
public ClassObject build() {
|
|
||||||
return new ClassObject(constantPool, fieldInfos, methodInfos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder<T> constantPoolCount(int constantPoolCount) {
|
public Builder<T> constantPoolCount(int constantPoolCount) {
|
||||||
constantPool = new ConstantPoolEntry[constantPoolCount - 1];
|
classObject.constantPool = new ConstantPoolEntry[constantPoolCount - 1];
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void constantPoolEntry(ConstantPoolEntry entry) {
|
void constantPoolEntry(ConstantPoolEntry entry) {
|
||||||
constantPool[constantPoolIndex++] = entry;
|
classObject.constantPool[constantPoolIndex++] = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder<T> fieldInfoCount(int fieldInfoCount) {
|
public Builder<T> fieldInfoCount(int fieldInfoCount) {
|
||||||
fieldInfos = new Info[fieldInfoCount];
|
classObject.fieldInfos = new Info[fieldInfoCount];
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder<T> fieldInfo(Info fieldInfo) {
|
public Builder<T> fieldInfo(Info fieldInfo) {
|
||||||
fieldInfos[fieldInfoIndex++] = fieldInfo;
|
classObject.fieldInfos[fieldInfoIndex++] = fieldInfo;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder<T> methodInfoCount(int methodInfoCount) {
|
public Builder<T> methodInfoCount(int methodInfoCount) {
|
||||||
methodInfos = new Info[methodInfoCount];
|
classObject.methodInfos = new Info[methodInfoCount];
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder<T> methodInfo(Info methodInfo) {
|
public Builder<T> methodInfo(Info methodInfo) {
|
||||||
methodInfos[methodInfoIndex++] = methodInfo;
|
classObject.methodInfos[methodInfoIndex++] = methodInfo;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ import java.io.IOException;
|
||||||
|
|
||||||
public class ClassReader extends DataReader {
|
public class ClassReader extends DataReader {
|
||||||
|
|
||||||
public <T> ClassObject parse(Class<T> type) {
|
public <T> ClassObject<T> parse(Class<T> type) {
|
||||||
final DataInputStream in = new DataInputStream(new BufferedInputStream(type.getResourceAsStream(getResourceName(type))));
|
DataInputStream in = new DataInputStream(new BufferedInputStream(type.getResourceAsStream(getResourceName(type))));
|
||||||
expect(in, 0xCAFEBABE);
|
expect(in, 0xCAFEBABE);
|
||||||
|
|
||||||
ClassObject.Builder<T> builder = new ClassObject.Builder<>();
|
ClassObject.Builder<T> builder = new ClassObject.Builder<>();
|
||||||
|
|
@ -19,9 +19,9 @@ public class ClassReader extends DataReader {
|
||||||
readConstantPool(in, builder);
|
readConstantPool(in, builder);
|
||||||
|
|
||||||
skip(in, 6); // u2 access_flags, u2 this_class, u2 super_class
|
skip(in, 6); // u2 access_flags, u2 this_class, u2 super_class
|
||||||
final int interfacesCount = readUnsignedShort(in);
|
int interfacesCount = readUnsignedShort(in);
|
||||||
skip(in, interfacesCount * 2); // interfaces[u2;]
|
skip(in, interfacesCount * 2); // interfaces[]
|
||||||
|
|
||||||
readFields(in, builder);
|
readFields(in, builder);
|
||||||
readMethods(in, builder);
|
readMethods(in, builder);
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ public class ClassReader extends DataReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void readConstantPool(DataInputStream in, ClassObject.Builder<T> builder) {
|
private <T> void readConstantPool(DataInputStream in, ClassObject.Builder<T> builder) {
|
||||||
final int constantPoolCount = readUnsignedShort(in);
|
int constantPoolCount = readUnsignedShort(in);
|
||||||
builder.constantPoolCount(constantPoolCount);
|
builder.constantPoolCount(constantPoolCount);
|
||||||
for (int i = 1; i < constantPoolCount; i++) {
|
for (int i = 1; i < constantPoolCount; i++) {
|
||||||
builder.constantPoolEntry(readConstantPoolEntry(in));
|
builder.constantPoolEntry(readConstantPoolEntry(in));
|
||||||
|
|
@ -37,7 +37,7 @@ public class ClassReader extends DataReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void readFields(DataInputStream in, ClassObject.Builder<T> builder) {
|
private <T> void readFields(DataInputStream in, ClassObject.Builder<T> builder) {
|
||||||
final int fieldInfoCount = readUnsignedShort(in);
|
int fieldInfoCount = readUnsignedShort(in);
|
||||||
builder.fieldInfoCount(fieldInfoCount);
|
builder.fieldInfoCount(fieldInfoCount);
|
||||||
for (int i = 0; i < fieldInfoCount; i++) {
|
for (int i = 0; i < fieldInfoCount; i++) {
|
||||||
builder.fieldInfo(readField(in));
|
builder.fieldInfo(readField(in));
|
||||||
|
|
@ -45,7 +45,7 @@ public class ClassReader extends DataReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void readMethods(DataInputStream in, ClassObject.Builder<T> builder) {
|
private <T> void readMethods(DataInputStream in, ClassObject.Builder<T> builder) {
|
||||||
final int methodInfoCount = readUnsignedShort(in);
|
int methodInfoCount = readUnsignedShort(in);
|
||||||
builder.methodInfoCount(methodInfoCount);
|
builder.methodInfoCount(methodInfoCount);
|
||||||
for (int i = 0; i < methodInfoCount; i++) {
|
for (int i = 0; i < methodInfoCount; i++) {
|
||||||
builder.methodInfo(readMethod(in));
|
builder.methodInfo(readMethod(in));
|
||||||
|
|
@ -53,7 +53,7 @@ public class ClassReader extends DataReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> Info readField(DataInputStream in) {
|
private <T> Info readField(DataInputStream in) {
|
||||||
final Info fieldInfo = new Info(readUnsignedShort(in), readUnsignedShort(in), readUnsignedShort(in), readUnsignedShort(in));
|
Info fieldInfo = new Info(readUnsignedShort(in), readUnsignedShort(in), readUnsignedShort(in), readUnsignedShort(in));
|
||||||
for (int i = 0; i < fieldInfo.getAttributesCount(); i++) {
|
for (int i = 0; i < fieldInfo.getAttributesCount(); i++) {
|
||||||
fieldInfo.add(readAttribute(in));
|
fieldInfo.add(readAttribute(in));
|
||||||
}
|
}
|
||||||
|
|
@ -62,7 +62,7 @@ public class ClassReader extends DataReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> Info readMethod(DataInputStream in) {
|
private <T> Info readMethod(DataInputStream in) {
|
||||||
final Info methodInfo = new Info(readUnsignedShort(in), readUnsignedShort(in), readUnsignedShort(in), readUnsignedShort(in));
|
Info methodInfo = new Info(readUnsignedShort(in), readUnsignedShort(in), readUnsignedShort(in), readUnsignedShort(in));
|
||||||
for (int i = 0; i < methodInfo.getAttributesCount(); i++) {
|
for (int i = 0; i < methodInfo.getAttributesCount(); i++) {
|
||||||
methodInfo.add(readAttribute(in));
|
methodInfo.add(readAttribute(in));
|
||||||
}
|
}
|
||||||
|
|
@ -71,9 +71,9 @@ public class ClassReader extends DataReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private AttributeInfo readAttribute(DataInputStream in) {
|
private AttributeInfo readAttribute(DataInputStream in) {
|
||||||
final int attributeNameIndex = readUnsignedShort(in);
|
int attributeNameIndex = readUnsignedShort(in);
|
||||||
final int attributeLength = readS32(in);
|
int attributeLength = readInt(in);
|
||||||
final byte[] info;
|
byte[] info;
|
||||||
if (attributeLength > 0) {
|
if (attributeLength > 0) {
|
||||||
info = new byte[attributeLength];
|
info = new byte[attributeLength];
|
||||||
try {
|
try {
|
||||||
|
|
@ -87,32 +87,30 @@ public class ClassReader extends DataReader {
|
||||||
return new AttributeInfo(attributeNameIndex, info);
|
return new AttributeInfo(attributeNameIndex, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConstantPoolEntry readConstantPoolEntry(DataInputStream in) {
|
private <T> ConstantPoolEntry readConstantPoolEntry(DataInputStream in) {
|
||||||
final byte tag = readByte(in);
|
byte tag = readByte(in);
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case 1: return new Utf8Entry(readString(in, readUnsignedShort(in)));
|
case 1: return readUtf8Entry(in);
|
||||||
case 2: throw new IllegalStateException("2: invalid classpool tag");
|
case 2: throw new IllegalStateException("2: invalid classpool tag");
|
||||||
case 3: return new IntEntry(readS32(in));
|
case 3: return new IntEntry(readInt(in));
|
||||||
case 4: return new FloatEntry(readF32(in));
|
case 4: return new FloatEntry(readFloat(in));
|
||||||
case 5: return new LongEntry(readS64(in));
|
case 5: return new LongEntry(readLong(in));
|
||||||
case 6: return new DoubleEntry(readF64(in));
|
case 6: return new DoubleEntry(readDouble(in));
|
||||||
case 7: return new ClassEntry(readUnsignedShort(in));
|
case 7: return new ClassEntry(readUnsignedShort(in));
|
||||||
case 8: return new StringEntry(readUnsignedShort(in));
|
case 8: return new StringEntry(readUnsignedShort(in));
|
||||||
case 9: return new FieldRefEntry(readUnsignedShort(in), readUnsignedShort(in));
|
case 9: return new FieldRefEntry(readShort(in), readShort(in));
|
||||||
case 10: return new MethodRefEntry(readUnsignedShort(in), readUnsignedShort(in));
|
case 10: return new MethodRefEntry(readUnsignedShort(in), readUnsignedShort(in));
|
||||||
case 11: return new InterfaceMethodRefEntry(readUnsignedShort(in), readUnsignedShort(in));
|
case 11: return new InterfaceMethodRefEntry(readUnsignedShort(in), readUnsignedShort(in));
|
||||||
case 12: return new NameAndTypeEntry(readUnsignedShort(in), readUnsignedShort(in));
|
case 12: return new NameAndTypeEntry(readUnsignedShort(in), readUnsignedShort(in));
|
||||||
case 15: return new MethodHandleEntry(readUnsignedShort(in), readUnsignedShort(in));
|
case 15: return new MethodHandleEntry(readUnsignedShort(in), readUnsignedShort(in));
|
||||||
case 16: return new MethodTypeEntry(readUnsignedShort(in));
|
case 16: return new MethodTypeEntry(readUnsignedShort(in));
|
||||||
case 18: return new InvokeDynamicEntry(readUnsignedShort(in), readUnsignedShort(in));
|
case 18: return new InvokeDynamicEntry(readUnsignedShort(in), readUnsignedShort(in));
|
||||||
case 19: return new ModuleEntry(readUnsignedShort(in));
|
|
||||||
case 20: return new PackageEntry(readUnsignedShort(in));
|
|
||||||
default: throw new IllegalStateException("invalid classpool");
|
default: throw new IllegalStateException("invalid classpool");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> String getResourceName(Class<T> type) {
|
protected <T> String getResourceName(Class<T> type) {
|
||||||
final StringBuilder typeName = new StringBuilder("/" + type.getName());
|
StringBuilder typeName = new StringBuilder("/" + type.getName());
|
||||||
|
|
||||||
for (int i = 0; i < typeName.length(); i++) {
|
for (int i = 0; i < typeName.length(); i++) {
|
||||||
if (typeName.charAt(i) == '.') {
|
if (typeName.charAt(i) == '.') {
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,15 @@
|
||||||
package nl.sander.jsontoy2.java;
|
package nl.sander.jsontoy2.java;
|
||||||
|
|
||||||
|
import nl.sander.jsontoy2.java.constantpool.Utf8Entry;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
|
||||||
* basic IO operations with runtime exceptions
|
|
||||||
*/
|
|
||||||
public class DataReader {
|
public class DataReader {
|
||||||
|
|
||||||
protected long readS64(DataInputStream in) {
|
protected float readFloat(DataInputStream in) {
|
||||||
try {
|
|
||||||
return in.readLong();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected float readF32(DataInputStream in) {
|
|
||||||
try {
|
try {
|
||||||
return in.readFloat();
|
return in.readFloat();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
@ -26,7 +17,29 @@ public class DataReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected double readF64(DataInputStream in) {
|
protected Utf8Entry readUtf8Entry(DataInputStream in) {
|
||||||
|
short length = readShort(in);
|
||||||
|
return new Utf8Entry(readString(in, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String readString(DataInputStream in, short length) {
|
||||||
|
try {
|
||||||
|
byte[] bytes = in.readNBytes(length);
|
||||||
|
return new String(bytes, StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long readLong(DataInputStream in) {
|
||||||
|
try {
|
||||||
|
return in.readLong();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double readDouble(DataInputStream in) {
|
||||||
try {
|
try {
|
||||||
return in.readDouble();
|
return in.readDouble();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
@ -34,6 +47,14 @@ public class DataReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected short readShort(DataInputStream in) {
|
||||||
|
try {
|
||||||
|
return in.readShort();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected int readUnsignedShort(DataInputStream in) {
|
protected int readUnsignedShort(DataInputStream in) {
|
||||||
try {
|
try {
|
||||||
return in.readUnsignedShort();
|
return in.readUnsignedShort();
|
||||||
|
|
@ -42,7 +63,7 @@ public class DataReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int readS32(DataInputStream in) {
|
protected int readInt(DataInputStream in) {
|
||||||
try {
|
try {
|
||||||
return in.readInt();
|
return in.readInt();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
@ -50,15 +71,6 @@ public class DataReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String readString(DataInputStream in, int length) {
|
|
||||||
try {
|
|
||||||
byte[] bytes = in.readNBytes(length);
|
|
||||||
return new String(bytes, StandardCharsets.UTF_8);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected byte readByte(DataInputStream in) {
|
protected byte readByte(DataInputStream in) {
|
||||||
try {
|
try {
|
||||||
return in.readByte();
|
return in.readByte();
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,23 @@
|
||||||
package nl.sander.jsontoy2.java;
|
package nl.sander.jsontoy2.java;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Method implements Comparable<Method> {
|
public class Method {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final List<String> parameterTypes;
|
private final String type;
|
||||||
private final String returnType;
|
|
||||||
|
|
||||||
public Method(String name, List<String> parameterTypes, String returnType) {
|
public Method(String name, String type) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.parameterTypes = parameterTypes;
|
this.type = type;
|
||||||
this.returnType = returnType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getReturnType() {
|
public String getType() {
|
||||||
return returnType;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -31,25 +26,19 @@ public class Method implements Comparable<Method> {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
Method field = (Method) o;
|
Method field = (Method) o;
|
||||||
return name.equals(field.name) &&
|
return name.equals(field.name) &&
|
||||||
returnType.equals(field.returnType);
|
type.equals(field.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(name, returnType);
|
return Objects.hash(name, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Method{" +
|
return "Method{" +
|
||||||
"name='" + name + '\'' +
|
"name='" + name + '\'' +
|
||||||
", parameterTypes=" + parameterTypes +
|
", type='" + type + '\'' +
|
||||||
", returnType='" + returnType + '\'' +
|
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(@NotNull Method o) {
|
|
||||||
return name.compareTo(o.name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
package nl.sander.jsontoy2.java.constantpool;
|
package nl.sander.jsontoy2.java.constantpool;
|
||||||
|
|
||||||
public class FieldRefEntry extends ConstantPoolEntry {
|
public class FieldRefEntry extends ConstantPoolEntry {
|
||||||
private final int classIndex;
|
private final short classIndex;
|
||||||
private final int nameAndTypeIndex;
|
private final short nameAndTypeIndex;
|
||||||
|
|
||||||
public FieldRefEntry(int classIndex, int nameAndTypeIndex) {
|
public FieldRefEntry(short classIndex, short nameAndTypeIndex) {
|
||||||
this.classIndex = classIndex;
|
this.classIndex = classIndex;
|
||||||
this.nameAndTypeIndex = nameAndTypeIndex;
|
this.nameAndTypeIndex = nameAndTypeIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getClassIndex() {
|
public short getClassIndex() {
|
||||||
return classIndex;
|
return classIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNameAndTypeIndex() {
|
public short getNameAndTypeIndex() {
|
||||||
return nameAndTypeIndex;
|
return nameAndTypeIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package nl.sander.jsontoy2.java.constantpool;
|
|
||||||
|
|
||||||
import nl.sander.jsontoy2.java.constantpool.ConstantPoolEntry;
|
|
||||||
|
|
||||||
public class ModuleEntry extends ConstantPoolEntry {
|
|
||||||
private final int nameIndex;
|
|
||||||
|
|
||||||
public ModuleEntry(int nameIndex) {
|
|
||||||
this.nameIndex = nameIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package nl.sander.jsontoy2.java.constantpool;
|
|
||||||
|
|
||||||
public class PackageEntry extends ConstantPoolEntry {
|
|
||||||
private final int nameIndex;
|
|
||||||
|
|
||||||
public PackageEntry(int nameIndex) {
|
|
||||||
this.nameIndex = nameIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package nl.sander.jsontoy2.javassist;
|
|
||||||
|
|
||||||
public class ClassCreationException extends RuntimeException {
|
|
||||||
public ClassCreationException(Throwable e) {
|
|
||||||
super(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClassCreationException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,183 +0,0 @@
|
||||||
package nl.sander.jsontoy2.javassist;
|
|
||||||
|
|
||||||
import javassist.*;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Facade for javassist functions
|
|
||||||
*/
|
|
||||||
public class Javassist {
|
|
||||||
private static final String STRING = "java.lang.String";
|
|
||||||
private static final String BOOLEAN = "java.lang.Boolean";
|
|
||||||
private static final String CHARACTER = "java.lang.Character";
|
|
||||||
private static final String BYTE = "java.lang.Byte";
|
|
||||||
private static final String DOUBLE = "java.lang.Double";
|
|
||||||
private static final String FLOAT = "java.lang.Float";
|
|
||||||
private static final String LONG = "java.lang.Long";
|
|
||||||
private static final String SHORT = "java.lang.Short";
|
|
||||||
private static final String INTEGER = "java.lang.Integer";
|
|
||||||
|
|
||||||
private final static Set<String> wrappersAndString = new HashSet<>(asList(BOOLEAN, CHARACTER, BYTE, DOUBLE, FLOAT, LONG, SHORT, INTEGER, STRING));
|
|
||||||
private final static Set<String> wrappers = new HashSet<>(asList(BOOLEAN, CHARACTER, BYTE, DOUBLE, FLOAT, LONG, SHORT, INTEGER));
|
|
||||||
|
|
||||||
private static final String COLLECTION = "java.util.Collection";
|
|
||||||
private static final String LIST = "java.util.List";
|
|
||||||
private static final String SET = "java.util.Set";
|
|
||||||
private static final String MAP = "java.util.Map";
|
|
||||||
|
|
||||||
private final static ClassPool pool = ClassPool.getDefault();
|
|
||||||
|
|
||||||
public static CtClass getTypeDefinition(Class<?> type) {
|
|
||||||
try {
|
|
||||||
return pool.get(type.getName());
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
throw new ClassCreationException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CtClass getTypeDefinition(String type) {
|
|
||||||
try {
|
|
||||||
return pool.get(type);
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
throw new ClassCreationException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CtClass createClass(String className, CtClass baseClass) {
|
|
||||||
return pool.makeClass(className, baseClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isPrimitiveOrWrapperOrString(CtClass beanClass) {
|
|
||||||
return beanClass.isPrimitive() || wrappersAndString.contains(beanClass.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isBasicType(CtClass beanClass) {
|
|
||||||
return isPrimitiveOrWrapperOrString(beanClass) || isList(beanClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isList(CtClass type) {
|
|
||||||
try {
|
|
||||||
List<CtClass> interfaces = new ArrayList<>(asList(type.getInterfaces()));
|
|
||||||
interfaces.add(type);
|
|
||||||
for (CtClass interfaze : interfaces) {
|
|
||||||
if (interfaze.getName().equals(COLLECTION) || interfaze.getName().equals(LIST) || interfaze.getName().equals(SET)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isCollection(CtClass type) {
|
|
||||||
try {
|
|
||||||
List<CtClass> interfaces = new ArrayList<>(asList(type.getInterfaces()));
|
|
||||||
interfaces.add(type);
|
|
||||||
for (CtClass interfaze : interfaces) {
|
|
||||||
if (interfaze.getName().equals(COLLECTION) || interfaze.getName().equals(LIST) || interfaze.getName().equals(SET) || interfaze.getName().equals(MAP)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isMap(CtClass type) throws NotFoundException {
|
|
||||||
List<CtClass> interfaces = new ArrayList<>(asList(type.getInterfaces()));
|
|
||||||
interfaces.add(type);
|
|
||||||
for (CtClass interfaze : interfaces) {
|
|
||||||
if (interfaze.getName().equals(MAP)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Retrieves getter methods from a class
|
|
||||||
*/
|
|
||||||
public static List<CtMethod> getGetters(CtClass type) {
|
|
||||||
List<CtMethod> methods = new ArrayList<>();
|
|
||||||
List<CtField> fields = getAllFields(type);
|
|
||||||
|
|
||||||
for (CtField field : fields) {
|
|
||||||
try {
|
|
||||||
CtMethod method = type.getMethod(getGetterMethod(field), getDescription(field));
|
|
||||||
if (Modifier.isPublic(method.getModifiers())) {
|
|
||||||
methods.add(method);
|
|
||||||
}
|
|
||||||
} catch (NotFoundException n) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return methods;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getGetterMethod(CtField field) {
|
|
||||||
return "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<CtField> getAllFields(CtClass beanClass) {
|
|
||||||
try {
|
|
||||||
List<CtField> allfields = new ArrayList<>(asList(beanClass.getDeclaredFields()));
|
|
||||||
if (beanClass.getSuperclass() != null) {
|
|
||||||
return getAllFields(beanClass.getSuperclass(), allfields);
|
|
||||||
}
|
|
||||||
return allfields;
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
throw new ClassCreationException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<CtField> getAllFields(CtClass beanClass, List<CtField> allfields) {
|
|
||||||
allfields.addAll(asList(beanClass.getDeclaredFields()));
|
|
||||||
|
|
||||||
return allfields;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getDescription(CtField field) throws NotFoundException {
|
|
||||||
if (field.getType().isArray()) {
|
|
||||||
return "()[" + innerClassName(field.getType().getName()) + ";";
|
|
||||||
} else if (!field.getType().isPrimitive()) {
|
|
||||||
return "()" + innerClassName(field.getType().getName()) + ";";
|
|
||||||
} else {
|
|
||||||
return "()" + asPrimitive(field.getType().getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String innerClassName(String name) {
|
|
||||||
return "L" + name.replaceAll("\\.", "/").replaceAll("\\[]", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String asPrimitive(String name) {
|
|
||||||
switch (name) {
|
|
||||||
case "int":
|
|
||||||
return "I";
|
|
||||||
case "byte":
|
|
||||||
return "B";
|
|
||||||
case "float":
|
|
||||||
return "F";
|
|
||||||
case "long":
|
|
||||||
return "J";
|
|
||||||
case "boolean":
|
|
||||||
return "Z";
|
|
||||||
case "char":
|
|
||||||
return "C";
|
|
||||||
case "double":
|
|
||||||
return "D";
|
|
||||||
case "short":
|
|
||||||
return "S";
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package nl.sander.jsontoy2.readers;
|
package nl.sander.jsontoy2.readers;
|
||||||
|
|
||||||
import nl.sander.jsontoy2.JsonValueReader;
|
|
||||||
|
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
@ -9,7 +7,7 @@ import java.time.format.DateTimeFormatterBuilder;
|
||||||
import java.time.temporal.ChronoField;
|
import java.time.temporal.ChronoField;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public abstract class AbstractDatesReader<T> extends JsonValueReader<T> {
|
public abstract class AbstractDatesReader<T> {
|
||||||
|
|
||||||
private static final ZoneId zone = ZoneId.systemDefault();
|
private static final ZoneId zone = ZoneId.systemDefault();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers;
|
||||||
import nl.sander.jsontoy2.JsonValueReader;
|
import nl.sander.jsontoy2.JsonValueReader;
|
||||||
import nl.sander.jsontoy2.Parser;
|
import nl.sander.jsontoy2.Parser;
|
||||||
|
|
||||||
public class BooleanReader extends JsonValueReader<Boolean> {
|
public class BooleanReader implements JsonValueReader<Boolean> {
|
||||||
@Override
|
@Override
|
||||||
public Boolean read(Parser parser) {
|
public Boolean read(Parser parser) {
|
||||||
return parser.parseBoolean();
|
return parser.parseBoolean();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers;
|
||||||
import nl.sander.jsontoy2.JsonValueReader;
|
import nl.sander.jsontoy2.JsonValueReader;
|
||||||
import nl.sander.jsontoy2.Parser;
|
import nl.sander.jsontoy2.Parser;
|
||||||
|
|
||||||
public class ByteReader extends JsonValueReader<Byte> {
|
public class ByteReader implements JsonValueReader<Byte> {
|
||||||
@Override
|
@Override
|
||||||
public Byte read(Parser parser) {
|
public Byte read(Parser parser) {
|
||||||
return parser.parseByte();
|
return parser.parseByte();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers;
|
||||||
import nl.sander.jsontoy2.JsonValueReader;
|
import nl.sander.jsontoy2.JsonValueReader;
|
||||||
import nl.sander.jsontoy2.Parser;
|
import nl.sander.jsontoy2.Parser;
|
||||||
|
|
||||||
public class CharReader extends JsonValueReader<Character> {
|
public class CharReader implements JsonValueReader<Character> {
|
||||||
@Override
|
@Override
|
||||||
public Character read(Parser parser) {
|
public Character read(Parser parser) {
|
||||||
return parser.parseCharacter();
|
return parser.parseCharacter();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
package nl.sander.jsontoy2.readers;
|
package nl.sander.jsontoy2.readers;
|
||||||
|
|
||||||
|
import nl.sander.jsontoy2.JsonValueReader;
|
||||||
import nl.sander.jsontoy2.Parser;
|
import nl.sander.jsontoy2.Parser;
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
public class DateReader extends AbstractDatesReader<Date> {
|
public class DateReader extends AbstractDatesReader<Date> implements JsonValueReader<Date> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date read(Parser parser) {
|
public Date read(Parser parser) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers;
|
||||||
import nl.sander.jsontoy2.JsonValueReader;
|
import nl.sander.jsontoy2.JsonValueReader;
|
||||||
import nl.sander.jsontoy2.Parser;
|
import nl.sander.jsontoy2.Parser;
|
||||||
|
|
||||||
public class DoubleReader extends JsonValueReader<Double> {
|
public class DoubleReader implements JsonValueReader<Double> {
|
||||||
@Override
|
@Override
|
||||||
public Double read(Parser parser) {
|
public Double read(Parser parser) {
|
||||||
return parser.parseDouble();
|
return parser.parseDouble();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers;
|
||||||
import nl.sander.jsontoy2.JsonValueReader;
|
import nl.sander.jsontoy2.JsonValueReader;
|
||||||
import nl.sander.jsontoy2.Parser;
|
import nl.sander.jsontoy2.Parser;
|
||||||
|
|
||||||
public class FloatReader extends JsonValueReader<Float> {
|
public class FloatReader implements JsonValueReader<Float> {
|
||||||
@Override
|
@Override
|
||||||
public Float read(Parser parser) {
|
public Float read(Parser parser) {
|
||||||
return parser.parseFloat();
|
return parser.parseFloat();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers;
|
||||||
import nl.sander.jsontoy2.JsonValueReader;
|
import nl.sander.jsontoy2.JsonValueReader;
|
||||||
import nl.sander.jsontoy2.Parser;
|
import nl.sander.jsontoy2.Parser;
|
||||||
|
|
||||||
public class IntegerReader extends JsonValueReader<Integer> {
|
public class IntegerReader implements JsonValueReader<Integer> {
|
||||||
@Override
|
@Override
|
||||||
public Integer read(Parser parser) {
|
public Integer read(Parser parser) {
|
||||||
return parser.parseInteger();
|
return parser.parseInteger();
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import nl.sander.jsontoy2.Parser;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public class ListReader extends JsonValueReader<List> {
|
public class ListReader implements JsonValueReader<List> {
|
||||||
@Override
|
@Override
|
||||||
public List<?> read(Parser parser) {
|
public List<?> read(Parser parser) {
|
||||||
return parser.parseArray();
|
return parser.parseArray();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
package nl.sander.jsontoy2.readers;
|
package nl.sander.jsontoy2.readers;
|
||||||
|
|
||||||
|
import nl.sander.jsontoy2.JsonValueReader;
|
||||||
import nl.sander.jsontoy2.Parser;
|
import nl.sander.jsontoy2.Parser;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
public class LocalDateTimeReader extends AbstractDatesReader<LocalDateTime> {
|
public class LocalDateTimeReader extends AbstractDatesReader<LocalDateTime> implements JsonValueReader<LocalDateTime> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LocalDateTime read(Parser parser) {
|
public LocalDateTime read(Parser parser) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers;
|
||||||
import nl.sander.jsontoy2.JsonValueReader;
|
import nl.sander.jsontoy2.JsonValueReader;
|
||||||
import nl.sander.jsontoy2.Parser;
|
import nl.sander.jsontoy2.Parser;
|
||||||
|
|
||||||
public class LongReader extends JsonValueReader<Long> {
|
public class LongReader implements JsonValueReader<Long> {
|
||||||
@Override
|
@Override
|
||||||
public Long read(Parser parser) {
|
public Long read(Parser parser) {
|
||||||
return parser.parseLong();
|
return parser.parseLong();
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import nl.sander.jsontoy2.Parser;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public class MapReader extends JsonValueReader<Map> {
|
public class MapReader implements JsonValueReader<Map> {
|
||||||
@Override
|
@Override
|
||||||
public Map read(Parser parser) {
|
public Map read(Parser parser) {
|
||||||
return parser.parseObject();
|
return parser.parseObject();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers;
|
||||||
import nl.sander.jsontoy2.JsonValueReader;
|
import nl.sander.jsontoy2.JsonValueReader;
|
||||||
import nl.sander.jsontoy2.Parser;
|
import nl.sander.jsontoy2.Parser;
|
||||||
|
|
||||||
public class ShortReader extends JsonValueReader<Short> {
|
public class ShortReader implements JsonValueReader<Short> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Short read(Parser parser) {
|
public Short read(Parser parser) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package nl.sander.jsontoy2.readers;
|
||||||
import nl.sander.jsontoy2.JsonValueReader;
|
import nl.sander.jsontoy2.JsonValueReader;
|
||||||
import nl.sander.jsontoy2.Parser;
|
import nl.sander.jsontoy2.Parser;
|
||||||
|
|
||||||
public class StringReader extends JsonValueReader<String> {
|
public class StringReader implements JsonValueReader<String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String read(Parser parser) {
|
public String read(Parser parser) {
|
||||||
|
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
package nl.sander.jsontoy2;
|
|
||||||
|
|
||||||
import nl.sander.jsontoy2.testobjects.BooleanBean;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
|
|
||||||
public class BeanWithBoolean {
|
|
||||||
@Test
|
|
||||||
public void testTrue() {
|
|
||||||
// Arrange
|
|
||||||
BooleanBean booleanBean = JsonReader.read(BooleanBean.class, "{\"value\": true, \"value2\": true}");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(new BooleanBean(true, Boolean.TRUE), booleanBean);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFalse() {
|
|
||||||
// Arrange
|
|
||||||
BooleanBean booleanBean = JsonReader.read(BooleanBean.class, "{\"value\": false, \"value2\": false}");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(new BooleanBean(false, Boolean.FALSE), booleanBean);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIllegalValues() {
|
|
||||||
// Assert
|
|
||||||
assertThrows(JsonParseException.class, () -> JsonReader.read(BooleanBean.class, "{\"value\": true, \"value2\": True}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,23 +7,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
public class Ints {
|
public class Ints {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleInt() {
|
public void testSimpleInt(){
|
||||||
assertEquals(1, JsonReader.read(Integer.class, "1"));
|
assertEquals(1,JsonReader.read(Integer.class,"1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimplePrimitiveInt() {
|
public void testSimplePrimitiveInt(){
|
||||||
assertEquals(1, JsonReader.read(int.class, "1"));
|
assertEquals(1,JsonReader.read(int.class,"1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleNegativeInt() {
|
public void testSimpleNegativeInt(){
|
||||||
assertEquals(-20001, JsonReader.read(Integer.class, "-20001"));
|
assertEquals(-20001,JsonReader.read(Integer.class,"-20001"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleNegativePrimitiveInt() {
|
public void testSimpleNegativePrimitiveInt(){
|
||||||
assertEquals(Integer.MIN_VALUE, JsonReader.read(int.class, "-2147483684"));
|
assertEquals(Integer.MIN_VALUE,JsonReader.read(int.class,"-2147483684"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -38,23 +38,23 @@ public class Lists {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void singleLong() {
|
public void singleInt() {
|
||||||
List<Long> list = JsonReader.read(List.class, " [ 1 ]");
|
List<Integer> list = JsonReader.read(List.class, " [ 1 ]");
|
||||||
List<Long> expected = List.of(1L);
|
List<Integer> expected = List.of(1);
|
||||||
assertEquals(expected, list);
|
assertEquals(expected, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipleLongs() {
|
public void multipleInts() {
|
||||||
List<Long> list = JsonReader.read(List.class, "[1,2]");
|
List<Integer> list = JsonReader.read(List.class, "[1,2]");
|
||||||
List<Long> expected = List.of(1L, 2L);
|
List<Integer> expected = List.of(1, 2);
|
||||||
assertEquals(expected, list);
|
assertEquals(expected, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void longDoubleBooleanString() {
|
public void intDoubleBooleanString() {
|
||||||
List<Integer> list = JsonReader.read(List.class, "[1, 2.5,false, \"hello jason\"]");
|
List<Integer> list = JsonReader.read(List.class, "[1, 2.5,false, \"hello jason\"]");
|
||||||
List<?> expected = List.of(1L, 2.5, false, "hello jason");
|
List<?> expected = List.of(1, 2.5, false, "hello jason");
|
||||||
assertEquals(expected, list);
|
assertEquals(expected, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ public class Maps {
|
||||||
Map<String, Object> map = JsonReader.read(Map.class, "{\"value1\" : \"jason\" ,\n \"value2\":1}");
|
Map<String, Object> map = JsonReader.read(Map.class, "{\"value1\" : \"jason\" ,\n \"value2\":1}");
|
||||||
Map<String, Object> expected = new HashMap<>();
|
Map<String, Object> expected = new HashMap<>();
|
||||||
expected.put("value1", "jason");
|
expected.put("value1", "jason");
|
||||||
expected.put("value2", 1L);
|
expected.put("value2", 1);
|
||||||
assertEquals(expected, map);
|
assertEquals(expected, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,9 +40,9 @@ public class Maps {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void singleLongs() {
|
public void singleInts() {
|
||||||
Map<String, Long> map = JsonReader.read(Map.class, " { \"1\":2 }");
|
Map<String, Integer> map = JsonReader.read(Map.class, " { \"1\":2 }");
|
||||||
Map<String, Long> expected = Collections.singletonMap("1", 2L);
|
Map<String, Integer> expected = Collections.singletonMap("1", 2);
|
||||||
assertEquals(expected, map);
|
assertEquals(expected, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ public class Maps {
|
||||||
public void listInMap() {
|
public void listInMap() {
|
||||||
Map<String, List> list = JsonReader.read(Map.class, " { \"list\" : [ 1 ] } ");
|
Map<String, List> list = JsonReader.read(Map.class, " { \"list\" : [ 1 ] } ");
|
||||||
Map<String, List> expected = new HashMap<>();
|
Map<String, List> expected = new HashMap<>();
|
||||||
expected.put("list", List.of(1L));
|
expected.put("list", List.of(1));
|
||||||
|
|
||||||
assertEquals(expected, list);
|
assertEquals(expected, list);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
package nl.sander.jsontoy2;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class ParserFactoryTest {
|
|
||||||
Parser parser1;
|
|
||||||
Parser parser2;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void differentThreadsHaveTheirOwnParser() throws InterruptedException {
|
|
||||||
Thread t1 = new Thread(() -> parser1 = ParserFactory.getParser(""));
|
|
||||||
Thread t2 = new Thread(() -> parser2 = ParserFactory.getParser(""));
|
|
||||||
t1.start();
|
|
||||||
t2.start();
|
|
||||||
t1.join();
|
|
||||||
t2.join();
|
|
||||||
|
|
||||||
assertNotSame(parser1, parser2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sameThreadCanReuseParser() {
|
|
||||||
Parser parser1 = ParserFactory.getParser("1");
|
|
||||||
Integer value1 = parser1.parseInteger();
|
|
||||||
Parser parser2 = ParserFactory.getParser("2");
|
|
||||||
Integer value2 = parser2.parseInteger();
|
|
||||||
|
|
||||||
assertSame(parser1, parser2);
|
|
||||||
assertEquals(1, value1);
|
|
||||||
assertEquals(2, value2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
package nl.sander.jsontoy2;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
|
|
||||||
class ParserTest {
|
|
||||||
@Test
|
|
||||||
public void testFloat() {
|
|
||||||
assertEquals(31.415927F, new Parser(getInputStream("31.415927")).parseFloat());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDouble() {
|
|
||||||
assertEquals(3.1415927D, new Parser(getInputStream("3.1415927")).parseDouble());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInt() {
|
|
||||||
assertEquals(31415927, new Parser(getInputStream("31415927")).parseInteger());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShort() {
|
|
||||||
assertEquals((short) 31415, new Parser(getInputStream("31415")).parseShort());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testByte() {
|
|
||||||
assertEquals((byte) 31, new Parser(getInputStream("31")).parseByte());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBooleanTrue() {
|
|
||||||
assertEquals(true, new Parser(getInputStream("true")).parseBoolean());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBooleanFalse() {
|
|
||||||
assertEquals(false, new Parser(getInputStream("false")).parseBoolean());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBooleanInvalid() {
|
|
||||||
assertThrows(JsonParseException.class, () -> new Parser(getInputStream("falsy")).parseBoolean());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testArray() {
|
|
||||||
assertEquals(List.of("3", "1", "4", "1"), new Parser(getInputStream("[\"3\",\"1\",\"4\",\"1\"]")).parseArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testArrayWithType() {
|
|
||||||
assertEquals(List.of(3D, 1D, 4D, 1D), new Parser(getInputStream("[3,1,4,1]")).parseArray(Double.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testObject() {
|
|
||||||
assertEquals(Map.of("pi", 3.1415927D), new Parser(getInputStream("{\"pi\":3.1415927}")).parseObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testObjectWithType() {
|
|
||||||
assertEquals(Map.of("pi", 3.1415927F), new Parser(getInputStream("{\"pi\":3.1415927}")).parseObject(PiBean.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testChar() {
|
|
||||||
assertEquals('3', new Parser(getInputStream("\"3\"")).parseCharacter());
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputStream getInputStream(String jsonString) {
|
|
||||||
return new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
static class PiBean {
|
|
||||||
private float pi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,169 +0,0 @@
|
||||||
package nl.sander.jsontoy2;
|
|
||||||
|
|
||||||
import nl.sander.jsontoy2.testobjects.*;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
public class WrapperObjectTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNested() {
|
|
||||||
SimpleBean bean = JsonReader.read(SimpleBean.class, "{\"data1\": \"value1\",\"data2\": \"value2\"}");
|
|
||||||
assertEquals(SimpleBean.class, bean.getClass());
|
|
||||||
|
|
||||||
assertEquals("value1", bean.getData1());
|
|
||||||
assertEquals("value2", bean.getData2());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBoolean() {
|
|
||||||
BooleanBean trueBean = JsonReader.read(BooleanBean.class, "{\"value\": true}");
|
|
||||||
assertTrue(trueBean.isValue());
|
|
||||||
// second call to read, must not recreate class definition, (it would not compile)
|
|
||||||
// so this test implicitly tests caching function too
|
|
||||||
BooleanBean falseBean = JsonReader.read(BooleanBean.class, "{\"value2\": false}");
|
|
||||||
assertFalse(falseBean.isValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testString() {
|
|
||||||
StringBean stringBean = JsonReader.read(StringBean.class, "{\"value\": \"haha\"}");
|
|
||||||
assertEquals("haha", stringBean.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInteger() {
|
|
||||||
IntegerBean integerBean = JsonReader.read(IntegerBean.class, "{\"value\": 1,\"value2\": 2}");
|
|
||||||
assertEquals(1, integerBean.getValue());
|
|
||||||
assertEquals(Integer.valueOf(2), integerBean.getValue2());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLong() {
|
|
||||||
LongBean longBean = JsonReader.read(LongBean.class, "{\"value\": 100000000000,\"value2\": 100000000000}");
|
|
||||||
assertEquals(100000000000L, longBean.getValue());
|
|
||||||
assertEquals(Long.valueOf(100000000000L), longBean.getValue2());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFloat() {
|
|
||||||
FloatBean floatBean = JsonReader.read(FloatBean.class, "{\"value\": 1.0,\"value2\": 100000000000}");
|
|
||||||
assertEquals(1.0F, floatBean.getValue(), 0.1F);
|
|
||||||
assertEquals(100000000000.0F, floatBean.getValue2(), 0.1F);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDouble() {
|
|
||||||
DoubleBean doubleBean = JsonReader.read(DoubleBean.class, "{\"value\": 1.0,\"value2\": 100000000000}");
|
|
||||||
assertEquals(1.0D, doubleBean.getValue(), 0.1D);
|
|
||||||
assertEquals(100000000000.0D, doubleBean.getValue2(), 0.1D);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShort() {
|
|
||||||
ShortBean shortBean = JsonReader.read(ShortBean.class, "{\"value\": 1,\"value2\": -11}");
|
|
||||||
assertEquals(1, shortBean.getValue());
|
|
||||||
assertEquals((short) -11, shortBean.getValue2().shortValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testByte() {
|
|
||||||
ByteBean byteBean = JsonReader.read(ByteBean.class, "{\"value\": 1,\"value2\": -11}");
|
|
||||||
assertEquals(1, byteBean.getValue());
|
|
||||||
assertEquals((byte) -11, byteBean.getValue2().shortValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testStringList() {
|
|
||||||
StringListBean listBean = JsonReader.read(StringListBean.class, "{\"value\": [\"a\",\"b\"]}");
|
|
||||||
assertEquals(Arrays.asList("a", "b"), listBean.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testStringSet() {
|
|
||||||
StringSetBean listBean = JsonReader.read(StringSetBean.class, "{\"value\": [\"a\",\"b\"]}");
|
|
||||||
assertEquals(new HashSet<>(Arrays.asList("a", "b")), listBean.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Test
|
|
||||||
//TODO enable and fix code
|
|
||||||
public void testIntegerList() {
|
|
||||||
IntegerListBean listBean = JsonReader.read(IntegerListBean.class, "{\"value\": [1,22]}");
|
|
||||||
assertEquals(Arrays.asList(1, 22), listBean.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Test
|
|
||||||
//TODO enable and fix code
|
|
||||||
public void testCharacterList() {
|
|
||||||
CharacterListBean listBean = JsonReader.read(CharacterListBean.class, "{\"value\": [\"a\", \"[\", \"^\"]}");
|
|
||||||
assertEquals(Arrays.asList('a', '[', '^'), listBean.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Test
|
|
||||||
//TODO enable and fix code
|
|
||||||
public void testShortList() {
|
|
||||||
ShortListBean listBean = JsonReader.read(ShortListBean.class, "{\"value\": [-1,0,1]}");
|
|
||||||
assertEquals(Arrays.asList((short) -1, (short) 0, (short) 1), listBean.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBooleanList() {
|
|
||||||
BooleanListBean listBean = JsonReader.read(BooleanListBean.class, "{\"value\": [true,false]}");
|
|
||||||
assertEquals(Arrays.asList(true, false), listBean.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Test
|
|
||||||
// public void testFloatList() {
|
|
||||||
// FloatListBean listBean = JsonReader.read(FloatListBean.class, "{\"value\": [-100.156,78.0]}");
|
|
||||||
// assertEquals(Arrays.asList(-100.156F, 78.0F), listBean.getValue());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// public void testByteList() {
|
|
||||||
// ByteListBean listBean = JsonReader.read(ByteListBean.class, "{\"value\": [-100,78]}");
|
|
||||||
// assertEquals(Arrays.asList((byte) -100, (byte) 78), listBean.getValue());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
|
|
||||||
// @Test
|
|
||||||
//TODO enable and fix code
|
|
||||||
public void testDoubleList() {
|
|
||||||
DoubleListBean listBean = JsonReader.read(DoubleListBean.class, "{\"value\": [-100.156,78.0]}");
|
|
||||||
assertEquals(Arrays.asList(-100.156D, 78.0D), listBean.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNestedBean() {
|
|
||||||
NestedBean nestedBean = JsonReader.read(NestedBean.class, "{\"value\": {\"value\": \"nested\"}}");
|
|
||||||
assertEquals(new NestedBean(new InnerBean("nested")), nestedBean);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testStringMap() {
|
|
||||||
StringMapBean actual = JsonReader.read(StringMapBean.class, "{\"map\": {\"a:\": \"b\", \"c:\" : \"d\" }}");
|
|
||||||
StringMapBean expected = new StringMapBean("a:", "b", "c:", "d");
|
|
||||||
assertEquals(expected, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLinkedList() {
|
|
||||||
LinkedListBean actual = JsonReader.read(LinkedListBean.class, "{\"list\": [\"a\"]}");
|
|
||||||
LinkedList<String> actualList = actual.getList();
|
|
||||||
|
|
||||||
assertEquals(List.of("a"), actualList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testArray() {
|
|
||||||
ArrayBean actual = JsonReader.read(ArrayBean.class, "{\"array\": [\"a\"]}");
|
|
||||||
|
|
||||||
assertEquals(1, actual.getArray().length);
|
|
||||||
assertEquals("a", actual.getArray()[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,30 +2,18 @@ package nl.sander.jsontoy2.java;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class ClassReaderTest {
|
public class ClassReaderTest {
|
||||||
|
|
||||||
//part of the test
|
|
||||||
public int field;
|
public int field;
|
||||||
|
|
||||||
public String method(String value, int[] value2, int value3) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadClass() {
|
public void testReadClass() {
|
||||||
ClassObject object = new ClassReader().parse(ClassReaderTest.class);
|
ClassObject<ClassReaderTest> object = new ClassReader().parse(ClassReaderTest.class);
|
||||||
assertEquals(Set.of(new Field("field", "I")), object.getFields());
|
assertEquals(Set.of(new Field("field", "I")), object.getFields());
|
||||||
assertEquals(new TreeSet<>(Set.of(
|
assertEquals(Set.of(new Method("<init>", "()V"), new Method("testReadClass", "()V")), object.getMethods());
|
||||||
new Method("method", List.of("Ljava/lang/String", "[I", "I"), "Ljava/lang/String"),
|
|
||||||
new Method("testReadClass", null, "V"),
|
|
||||||
new Method("<init>", null, "V"))
|
|
||||||
), new TreeSet<>(object.getMethods()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
package nl.sander.jsontoy2.javassist;
|
|
||||||
|
|
||||||
import javassist.CtClass;
|
|
||||||
import javassist.CtMethod;
|
|
||||||
import javassist.NotFoundException;
|
|
||||||
import nl.sander.jsontoy2.testobjects.BooleanBean;
|
|
||||||
import nl.sander.jsontoy2.testobjects.SimpleBean;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
public class JavassistTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getTypeDefinitionForClass() throws NotFoundException {
|
|
||||||
// Act
|
|
||||||
CtClass typeDefinition = Javassist.getTypeDefinition(BooleanBean.class);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals("setValue", typeDefinition.getDeclaredMethod("setValue").getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getTypeDefinitionForClassname() throws NotFoundException {
|
|
||||||
// Act
|
|
||||||
CtClass typeDefinition = Javassist.getTypeDefinition("nl.sander.jsontoy2.testobjects.BooleanBean");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertNotNull(typeDefinition);
|
|
||||||
assertEquals("BooleanBean", typeDefinition.getSimpleName());
|
|
||||||
assertEquals("nl.sander.jsontoy2.testobjects.BooleanBean", typeDefinition.getName());
|
|
||||||
assertEquals("value", typeDefinition.getDeclaredField("value").getName());
|
|
||||||
assertEquals("setValue", typeDefinition.getDeclaredMethod("setValue").getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void createClass() {
|
|
||||||
// Act
|
|
||||||
CtClass newClass = Javassist.createClass("UberClass", Javassist.getTypeDefinition(Object.class));
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals("UberClass", newClass.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isCollection() {
|
|
||||||
// Act/ Assert
|
|
||||||
assertTrue(Javassist.isCollection(Javassist.getTypeDefinition(List.class)));
|
|
||||||
assertTrue(Javassist.isCollection(Javassist.getTypeDefinition(HashMap.class)));
|
|
||||||
assertFalse(Javassist.isCollection(Javassist.getTypeDefinition(String.class)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getGetterMethod() {
|
|
||||||
// Arrange
|
|
||||||
CtClass typeDefinition = Javassist.getTypeDefinition(SimpleBean.class);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
List<CtMethod> getterMethods = Javassist.getGetters(typeDefinition);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertEquals(2, getterMethods.size());
|
|
||||||
assertEquals("getData1", getterMethods.get(0).getName());
|
|
||||||
assertEquals("getData2", getterMethods.get(1).getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class ArrayBean {
|
|
||||||
private String[] array;
|
|
||||||
|
|
||||||
public void setArray(String[] array) {
|
|
||||||
this.array = array;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getArray() {
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class BooleanBean {
|
|
||||||
private boolean value;
|
|
||||||
private Boolean value2;
|
|
||||||
|
|
||||||
public BooleanBean() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBean(boolean value, Boolean value2) {
|
|
||||||
this.value = value;
|
|
||||||
this.value2 = value2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(boolean value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean getValue2() {
|
|
||||||
return value2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue2(Boolean value2) {
|
|
||||||
this.value2 = value2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
BooleanBean that = (BooleanBean) o;
|
|
||||||
return value == that.value &&
|
|
||||||
value2.equals(that.value2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(value, value2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class BooleanListBean {
|
|
||||||
private List<Boolean> value;
|
|
||||||
|
|
||||||
public List<Boolean> getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(List<Boolean> value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "BooleanListBean{" +
|
|
||||||
"value=" + value +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class ByteBean {
|
|
||||||
private byte value;
|
|
||||||
private Byte value2;
|
|
||||||
|
|
||||||
public ByteBean() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(byte value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Byte getValue2() {
|
|
||||||
return value2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue2(Byte value2) {
|
|
||||||
this.value2 = value2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class ByteListBean {
|
|
||||||
private List<Byte> value;
|
|
||||||
|
|
||||||
public List<Byte> getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(List<Byte> value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class CharacterListBean {
|
|
||||||
private List<Character> value;
|
|
||||||
|
|
||||||
public List<Character> getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(List<Character> value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class DoubleBean {
|
|
||||||
private double value;
|
|
||||||
private Double value2;
|
|
||||||
|
|
||||||
public DoubleBean() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(double value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getValue2() {
|
|
||||||
return value2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue2(Double value2) {
|
|
||||||
this.value2 = value2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import nl.sander.jsontoy2.JsonReader;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class DoubleListBean {
|
|
||||||
private List<Double> value;
|
|
||||||
|
|
||||||
public List<Double> getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(List<Double> value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTrue() {
|
|
||||||
assertEquals(true, JsonReader.read(Boolean.class, "true"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class FloatBean {
|
|
||||||
private float value;
|
|
||||||
private Float value2;
|
|
||||||
|
|
||||||
public FloatBean() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public FloatBean(float value, Float value2) {
|
|
||||||
this.value = value;
|
|
||||||
this.value2 = value2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(float value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Float getValue2() {
|
|
||||||
return value2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue2(Float value2) {
|
|
||||||
this.value2 = value2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class FloatListBean {
|
|
||||||
private List<Float> value;
|
|
||||||
|
|
||||||
public List<Float> getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(List<Float> value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class InnerBean {
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
public InnerBean() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public InnerBean(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
InnerBean innerBean = (InnerBean) o;
|
|
||||||
return value.equals(innerBean.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "InnerBean{" +
|
|
||||||
"value='" + value + '\'' +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class IntegerBean {
|
|
||||||
private int value;
|
|
||||||
private Integer value2;
|
|
||||||
|
|
||||||
public IntegerBean() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(int value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getValue2() {
|
|
||||||
return value2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue2(Integer value2) {
|
|
||||||
this.value2 = value2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class IntegerListBean {
|
|
||||||
private List<Integer> value;
|
|
||||||
|
|
||||||
public List<Integer> getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(List<Integer> value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class LinkedListBean {
|
|
||||||
private LinkedList<String> list;
|
|
||||||
|
|
||||||
public LinkedList<String> getList() {
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setList(LinkedList<String> list) {
|
|
||||||
this.list = list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class LongBean {
|
|
||||||
private long value;
|
|
||||||
private Long value2;
|
|
||||||
|
|
||||||
public LongBean() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(long value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getValue2() {
|
|
||||||
return value2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue2(Long value2) {
|
|
||||||
this.value2 = value2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class NestedBean {
|
|
||||||
|
|
||||||
private InnerBean value;
|
|
||||||
private String empty;
|
|
||||||
|
|
||||||
public NestedBean() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public NestedBean(InnerBean value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InnerBean getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(InnerBean value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEmpty(String empty) {
|
|
||||||
this.empty = empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
NestedBean that = (NestedBean) o;
|
|
||||||
return value.equals(that.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "NestedBean{" +
|
|
||||||
"value=" + value +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class ShortBean {
|
|
||||||
private short value;
|
|
||||||
private Short value2;
|
|
||||||
|
|
||||||
public ShortBean() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public short getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(short value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Short getValue2() {
|
|
||||||
return value2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue2(Short value2) {
|
|
||||||
this.value2 = value2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class ShortListBean {
|
|
||||||
private List<Short> value;
|
|
||||||
|
|
||||||
public List<Short> getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(List<Short> value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class SimpleBean {
|
|
||||||
String data1;
|
|
||||||
String data2;
|
|
||||||
|
|
||||||
public String getData1() {
|
|
||||||
return data1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setData1(String data1) {
|
|
||||||
this.data1 = data1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getData2() {
|
|
||||||
return data2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setData2(String data2) {
|
|
||||||
this.data2 = data2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class StringBean {
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class StringListBean {
|
|
||||||
private List<String> value;
|
|
||||||
|
|
||||||
public List<String> getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(List<String> value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class StringMapBean {
|
|
||||||
|
|
||||||
private Map<String, String> map = new HashMap<>();
|
|
||||||
|
|
||||||
public StringMapBean() {
|
|
||||||
this.map = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringMapBean(String... keysAndValues) {
|
|
||||||
if (keysAndValues.length % 2 == 1) {
|
|
||||||
throw new IllegalArgumentException("uneven number of arguments is not allowed here");
|
|
||||||
}
|
|
||||||
for (int i = 0; i < keysAndValues.length; ) {
|
|
||||||
String key = keysAndValues[i++];
|
|
||||||
String value = keysAndValues[i++];
|
|
||||||
map.put(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMap(Map<String, String> map) {
|
|
||||||
this.map = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getMap() {
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
StringMapBean that = (StringMapBean) o;
|
|
||||||
return map.equals(that.map);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package nl.sander.jsontoy2.testobjects;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* test object
|
|
||||||
*/
|
|
||||||
public class StringSetBean {
|
|
||||||
private Set<String> value;
|
|
||||||
|
|
||||||
public Set<String> getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(Set<String> value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue