Added utility method to return all declared super class fields
This commit is contained in:
parent
eac41eec12
commit
1bc97d88ee
6 changed files with 179 additions and 60 deletions
|
|
@ -28,12 +28,12 @@ import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class include some utility functions for classes
|
* This class include some utility functions for classes
|
||||||
*
|
|
||||||
* User: Ziver
|
|
||||||
*/
|
*/
|
||||||
public class ClassUtil {
|
public class ClassUtil {
|
||||||
/** A Set that contains possible wrapper objects for primitives **/
|
/** A Set that contains possible wrapper objects for primitives **/
|
||||||
|
|
@ -217,4 +217,31 @@ public class ClassUtil {
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses the class hierarchy and collects all fields.
|
||||||
|
*
|
||||||
|
* @param clazz is the class to return fields from.
|
||||||
|
* @return a List including all fields contained from the class and its super classes.
|
||||||
|
*/
|
||||||
|
public static List<Field> getAllDeclaredFields(Class<?> clazz) {
|
||||||
|
return getAllDeclaredFields(clazz, Object.class);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Traverses the class hierarchy and collects all fields.
|
||||||
|
*
|
||||||
|
* @param clazz is the class to return fields from.
|
||||||
|
* @param upToClass a top limit class where traversal will end not including fields from this class.
|
||||||
|
* @return a List including all fields contained from the class and its super classes.
|
||||||
|
*/
|
||||||
|
public static List<Field> getAllDeclaredFields(Class<?> clazz, Class<?> upToClass) {
|
||||||
|
List<Field> fields = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Class<?> currentClass = clazz; currentClass != upToClass; currentClass = currentClass.getSuperclass()) {
|
||||||
|
Collections.addAll(fields, currentClass.getDeclaredFields());
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import zutil.ClassUtil;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -53,7 +54,6 @@ class DBBeanConfig{
|
||||||
private DBBeanConfig() { }
|
private DBBeanConfig() { }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the configuration object for the specified class
|
* @return the configuration object for the specified class
|
||||||
*/
|
*/
|
||||||
|
|
@ -66,39 +66,44 @@ class DBBeanConfig{
|
||||||
/**
|
/**
|
||||||
* Caches the fields
|
* Caches the fields
|
||||||
*/
|
*/
|
||||||
private static void initBeanConfig(Class<? extends DBBean> c) {
|
private static void initBeanConfig(Class<? extends DBBean> clazz) {
|
||||||
DBBeanConfig config = new DBBeanConfig();
|
DBBeanConfig config = new DBBeanConfig();
|
||||||
|
|
||||||
// Find the table name
|
// Find the table name
|
||||||
DBBean.DBTable tableAnn = c.getAnnotation(DBBean.DBTable.class);
|
|
||||||
|
DBBean.DBTable tableAnn = clazz.getAnnotation(DBBean.DBTable.class);
|
||||||
if (tableAnn != null) {
|
if (tableAnn != null) {
|
||||||
config.tableName = tableAnn.value();
|
config.tableName = tableAnn.value();
|
||||||
config.idColumnName = tableAnn.idColumn();
|
config.idColumnName = tableAnn.idColumn();
|
||||||
} else {
|
} else {
|
||||||
config.tableName = c.getSimpleName();
|
config.tableName = clazz.getSimpleName();
|
||||||
config.idColumnName = "id";
|
config.idColumnName = "id";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get fields
|
||||||
|
|
||||||
|
List<Field> fields;
|
||||||
|
if (tableAnn != null && tableAnn.superBean())
|
||||||
|
fields = ClassUtil.getAllDeclaredFields(clazz, DBBean.class);
|
||||||
|
else
|
||||||
|
fields = Arrays.asList(clazz.getDeclaredFields());
|
||||||
|
|
||||||
// Add the fields in the bean and all the super classes fields
|
// Add the fields in the bean and all the super classes fields
|
||||||
for (Class<?> cc = c; cc != DBBean.class; cc = cc.getSuperclass()) {
|
for (Field field : fields) {
|
||||||
Field[] fields = cc.getDeclaredFields();
|
int mod = field.getModifiers();
|
||||||
for (Field field : fields) {
|
if (!Modifier.isTransient(mod) &&
|
||||||
int mod = field.getModifiers();
|
!Modifier.isFinal(mod) &&
|
||||||
if (!Modifier.isTransient(mod) &&
|
!Modifier.isStatic(mod) &&
|
||||||
!Modifier.isFinal(mod) &&
|
!config.fields.contains(field)) {
|
||||||
!Modifier.isStatic(mod) &&
|
if (List.class.isAssignableFrom(field.getType()) &&
|
||||||
!config.fields.contains(field)) {
|
field.getAnnotation(DBBean.DBLinkTable.class) != null)
|
||||||
if (List.class.isAssignableFrom(field.getType()) &&
|
config.subBeanFields.add(new DBBeanSubBeanConfig(field));
|
||||||
field.getAnnotation(DBBean.DBLinkTable.class) != null)
|
else
|
||||||
config.subBeanFields.add(new DBBeanSubBeanConfig(field));
|
config.fields.add(new DBBeanFieldConfig(field));
|
||||||
else
|
|
||||||
config.fields.add(new DBBeanFieldConfig(field));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (tableAnn == null || !tableAnn.superBean())
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
beanConfigs.put(c.getName(), config);
|
beanConfigs.put(clazz.getName(), config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
package zutil.parser;
|
package zutil.parser;
|
||||||
|
|
||||||
|
import zutil.ClassUtil;
|
||||||
import zutil.io.file.FileUtil;
|
import zutil.io.file.FileUtil;
|
||||||
import zutil.log.LogUtil;
|
import zutil.log.LogUtil;
|
||||||
import zutil.struct.MutableInt;
|
import zutil.struct.MutableInt;
|
||||||
|
|
@ -412,7 +413,7 @@ public class Templator {
|
||||||
else {
|
else {
|
||||||
// Using a loop as the direct lookup throws a exception if no field was found
|
// Using a loop as the direct lookup throws a exception if no field was found
|
||||||
// So this is probably a bit faster
|
// So this is probably a bit faster
|
||||||
for (Field field : obj.getClass().getDeclaredFields()) { // Only look for public fields
|
for (Field field : ClassUtil.getAllDeclaredFields(obj.getClass())) { // Only look for public fields
|
||||||
if (field.getName().equals(attrib)) {
|
if (field.getName().equals(attrib)) {
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
return field.get(obj);
|
return field.get(obj);
|
||||||
|
|
|
||||||
|
|
@ -209,21 +209,23 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C
|
||||||
if (json.getString("@object_id") != null && objectCache.containsKey(json.getInt(MD_OBJECT_ID)))
|
if (json.getString("@object_id") != null && objectCache.containsKey(json.getInt(MD_OBJECT_ID)))
|
||||||
return objectCache.get(json.getInt(MD_OBJECT_ID));
|
return objectCache.get(json.getInt(MD_OBJECT_ID));
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
// Resolve the class
|
// Resolve the class
|
||||||
Object obj;
|
// ------------------------------------------------
|
||||||
// Try using explicit class
|
|
||||||
|
Class<?> objClass;
|
||||||
|
|
||||||
|
// Try using explicit class from target
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
obj = type.getDeclaredConstructor().newInstance();
|
objClass = type;
|
||||||
}
|
}
|
||||||
// Try using metadata
|
// Try using JSON metadata
|
||||||
else if (json.getString(MD_CLASS) != null) {
|
else if (json.getString(MD_CLASS) != null) {
|
||||||
Class<?> objClass = Class.forName(json.getString(MD_CLASS));
|
objClass = Class.forName(json.getString(MD_CLASS));
|
||||||
obj = objClass.getDeclaredConstructor().newInstance();
|
|
||||||
}
|
}
|
||||||
// Search for registered classes
|
// Search for registered classes
|
||||||
else if (registeredClasses.containsKey(key)) {
|
else if (registeredClasses.containsKey(key)) {
|
||||||
Class<?> objClass = registeredClasses.get(key);
|
objClass = registeredClasses.get(key);
|
||||||
obj = objClass.getDeclaredConstructor().newInstance();
|
|
||||||
}
|
}
|
||||||
// Unknown class
|
// Unknown class
|
||||||
else {
|
else {
|
||||||
|
|
@ -231,19 +233,38 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read all fields from the new object instance
|
// ------------------------------------------------
|
||||||
for (Field field : obj.getClass().getDeclaredFields()) {
|
// Instantiate object
|
||||||
if ((field.getModifiers() & Modifier.STATIC) == 0 &&
|
// ------------------------------------------------
|
||||||
(field.getModifiers() & Modifier.TRANSIENT) == 0 &&
|
|
||||||
json.get(field.getName()) != null) {
|
Object obj = null;
|
||||||
// Parse field
|
|
||||||
field.setAccessible(true);
|
// Date and time objects
|
||||||
field.set(obj, readType(
|
if (Date.class.isAssignableFrom(objClass)) {
|
||||||
field.getType(),
|
obj = new Date(json.getLong("timestamp"));
|
||||||
ClassUtil.getGenericClasses(field),
|
}
|
||||||
field.get(obj),
|
else if (Calendar.class.isAssignableFrom(objClass)) {
|
||||||
field.getName(),
|
obj = Calendar.getInstance();
|
||||||
json.get(field.getName())));
|
((Calendar) obj).setTimeInMillis(json.getLong("timestamp"));
|
||||||
|
}
|
||||||
|
// Instantiate generic object
|
||||||
|
else{
|
||||||
|
obj = objClass.getDeclaredConstructor().newInstance();
|
||||||
|
|
||||||
|
// Read all fields from the new object instance
|
||||||
|
for (Field field : ClassUtil.getAllDeclaredFields(obj.getClass())) {
|
||||||
|
if ((field.getModifiers() & Modifier.STATIC) == 0 &&
|
||||||
|
(field.getModifiers() & Modifier.TRANSIENT) == 0 &&
|
||||||
|
json.get(field.getName()) != null) {
|
||||||
|
// Parse field
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(obj, readType(
|
||||||
|
field.getType(),
|
||||||
|
ClassUtil.getGenericClasses(field),
|
||||||
|
field.get(obj),
|
||||||
|
field.getName(),
|
||||||
|
json.get(field.getName())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add object to the cache
|
// Add object to the cache
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,12 @@ import java.io.*;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static zutil.parser.json.JSONObjectInputStream.MD_CLASS;
|
import static zutil.parser.json.JSONObjectInputStream.MD_CLASS;
|
||||||
import static zutil.parser.json.JSONObjectInputStream.MD_OBJECT_ID;
|
import static zutil.parser.json.JSONObjectInputStream.MD_OBJECT_ID;
|
||||||
|
|
||||||
|
|
||||||
public class JSONObjectOutputStream extends OutputStream implements ObjectOutput, Closeable{
|
public class JSONObjectOutputStream extends OutputStream implements ObjectOutput, Closeable{
|
||||||
/** If the generated JSON should contain class def meta-data **/
|
/** If the generated JSON should contain class def meta-data **/
|
||||||
private boolean generateMetaData = true;
|
private boolean generateMetaData = true;
|
||||||
|
|
@ -148,23 +147,32 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
else { // Miss
|
else { // Miss
|
||||||
objectCache.put(obj, objectCache.size()+1);
|
objectCache.put(obj, objectCache.size() + 1);
|
||||||
root.set(MD_OBJECT_ID, objectCache.size());
|
root.set(MD_OBJECT_ID, objectCache.size());
|
||||||
}
|
}
|
||||||
root.set(MD_CLASS, obj.getClass().getName());
|
root.set(MD_CLASS, obj.getClass().getName());
|
||||||
}
|
}
|
||||||
// Add all the fields to the DataNode
|
|
||||||
for (Field field : obj.getClass().getDeclaredFields()) {
|
|
||||||
if (! Modifier.isStatic(field.getModifiers()) &&
|
|
||||||
! Modifier.isTransient(field.getModifiers())) {
|
|
||||||
field.setAccessible(true);
|
|
||||||
Object fieldObj = field.get(obj);
|
|
||||||
|
|
||||||
// has object a value?
|
// Date and time objects
|
||||||
if (ignoreNullFields && fieldObj == null)
|
if (Date.class.isAssignableFrom(objClass)) {
|
||||||
continue;
|
root.set("timestamp", ((Date) obj).getTime());
|
||||||
else
|
}
|
||||||
root.set(field.getName(), getDataNode(fieldObj));
|
else if (Calendar.class.isAssignableFrom(objClass)) {
|
||||||
|
root.set("timestamp", ((Calendar) obj).getTimeInMillis());
|
||||||
|
}
|
||||||
|
else { // Generic class, Add all the fields to the DataNode
|
||||||
|
for (Field field : ClassUtil.getAllDeclaredFields(obj.getClass())) {
|
||||||
|
if (!Modifier.isStatic(field.getModifiers()) &&
|
||||||
|
!Modifier.isTransient(field.getModifiers())) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object fieldObj = field.get(obj);
|
||||||
|
|
||||||
|
// has object a value?
|
||||||
|
if (ignoreNullFields && fieldObj == null)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
root.set(field.getName(), getDataNode(fieldObj));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
57
test/zutil/ClassUtilTest.java
Normal file
57
test/zutil/ClassUtilTest.java
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
package zutil;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class ClassUtilTest extends TestCase {
|
||||||
|
|
||||||
|
public void testGetAllDeclaredFields() {
|
||||||
|
List<Field> fields = ClassUtil.getAllDeclaredFields(TestClass.class);
|
||||||
|
List<String> fieldNames = getFieldNames(fields);
|
||||||
|
|
||||||
|
assertEquals(4, fields.size());
|
||||||
|
assertTrue(fieldNames.contains("superPrivateInt"));
|
||||||
|
assertTrue(fieldNames.contains("superPublicInt"));
|
||||||
|
assertTrue(fieldNames.contains("protectedInt"));
|
||||||
|
assertTrue(fieldNames.contains("publicInt"));
|
||||||
|
|
||||||
|
fields = ClassUtil.getAllDeclaredFields(TestClass.class, TestSuperClass.class);
|
||||||
|
fieldNames = getFieldNames(fields);
|
||||||
|
|
||||||
|
assertEquals(2, fields.size());
|
||||||
|
assertFalse(fieldNames.contains("superPrivateInt"));
|
||||||
|
assertFalse(fieldNames.contains("superPublicInt"));
|
||||||
|
assertTrue(fieldNames.contains("protectedInt"));
|
||||||
|
assertTrue(fieldNames.contains("publicInt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Utilities
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
private List<String> getFieldNames(List<Field> fields) {
|
||||||
|
List<String> names = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Field field : fields)
|
||||||
|
names.add(field.getName());
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Test Classes
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
public static class TestSuperClass {
|
||||||
|
private int superPrivateInt = 10;
|
||||||
|
public int superPublicInt = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestClass extends TestSuperClass {
|
||||||
|
protected int protectedInt = 20;
|
||||||
|
public int publicInt = 21;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue