Added utility method to return all declared super class fields

This commit is contained in:
Ziver Koc 2021-07-19 23:50:48 +02:00
parent eac41eec12
commit 1bc97d88ee
6 changed files with 179 additions and 60 deletions

View file

@ -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;
}
} }

View file

@ -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);
} }

View file

@ -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);

View file

@ -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

View file

@ -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));
}
} }
} }
} }

View 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;
}
}