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.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
/**
* This class include some utility functions for classes
*
* User: Ziver
*/
public class ClassUtil {
/** A Set that contains possible wrapper objects for primitives **/
@ -217,4 +217,31 @@ public class ClassUtil {
}
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.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@ -53,7 +54,6 @@ class DBBeanConfig{
private DBBeanConfig() { }
/**
* @return the configuration object for the specified class
*/
@ -66,21 +66,29 @@ class DBBeanConfig{
/**
* Caches the fields
*/
private static void initBeanConfig(Class<? extends DBBean> c) {
private static void initBeanConfig(Class<? extends DBBean> clazz) {
DBBeanConfig config = new DBBeanConfig();
// Find the table name
DBBean.DBTable tableAnn = c.getAnnotation(DBBean.DBTable.class);
DBBean.DBTable tableAnn = clazz.getAnnotation(DBBean.DBTable.class);
if (tableAnn != null) {
config.tableName = tableAnn.value();
config.idColumnName = tableAnn.idColumn();
} else {
config.tableName = c.getSimpleName();
config.tableName = clazz.getSimpleName();
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
for (Class<?> cc = c; cc != DBBean.class; cc = cc.getSuperclass()) {
Field[] fields = cc.getDeclaredFields();
for (Field field : fields) {
int mod = field.getModifiers();
if (!Modifier.isTransient(mod) &&
@ -94,11 +102,8 @@ class DBBeanConfig{
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;
import zutil.ClassUtil;
import zutil.io.file.FileUtil;
import zutil.log.LogUtil;
import zutil.struct.MutableInt;
@ -412,7 +413,7 @@ public class Templator {
else {
// Using a loop as the direct lookup throws a exception if no field was found
// 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)) {
field.setAccessible(true);
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)))
return objectCache.get(json.getInt(MD_OBJECT_ID));
// ------------------------------------------------
// Resolve the class
Object obj;
// Try using explicit class
// ------------------------------------------------
Class<?> objClass;
// Try using explicit class from target
if (type != null) {
obj = type.getDeclaredConstructor().newInstance();
objClass = type;
}
// Try using metadata
// Try using JSON metadata
else if (json.getString(MD_CLASS) != null) {
Class<?> objClass = Class.forName(json.getString(MD_CLASS));
obj = objClass.getDeclaredConstructor().newInstance();
objClass = Class.forName(json.getString(MD_CLASS));
}
// Search for registered classes
else if (registeredClasses.containsKey(key)) {
Class<?> objClass = registeredClasses.get(key);
obj = objClass.getDeclaredConstructor().newInstance();
objClass = registeredClasses.get(key);
}
// Unknown class
else {
@ -231,8 +233,26 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C
return null;
}
// ------------------------------------------------
// Instantiate object
// ------------------------------------------------
Object obj = null;
// Date and time objects
if (Date.class.isAssignableFrom(objClass)) {
obj = new Date(json.getLong("timestamp"));
}
else if (Calendar.class.isAssignableFrom(objClass)) {
obj = Calendar.getInstance();
((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 : obj.getClass().getDeclaredFields()) {
for (Field field : ClassUtil.getAllDeclaredFields(obj.getClass())) {
if ((field.getModifiers() & Modifier.STATIC) == 0 &&
(field.getModifiers() & Modifier.TRANSIENT) == 0 &&
json.get(field.getName()) != null) {
@ -246,6 +266,7 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C
json.get(field.getName())));
}
}
}
// Add object to the cache
if (json.getString(MD_OBJECT_ID) != null)
objectCache.put(json.getInt(MD_OBJECT_ID), obj);

View file

@ -34,13 +34,12 @@ import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import static zutil.parser.json.JSONObjectInputStream.MD_CLASS;
import static zutil.parser.json.JSONObjectInputStream.MD_OBJECT_ID;
public class JSONObjectOutputStream extends OutputStream implements ObjectOutput, Closeable{
/** If the generated JSON should contain class def meta-data **/
private boolean generateMetaData = true;
@ -153,8 +152,16 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput
}
root.set(MD_CLASS, obj.getClass().getName());
}
// Add all the fields to the DataNode
for (Field field : obj.getClass().getDeclaredFields()) {
// Date and time objects
if (Date.class.isAssignableFrom(objClass)) {
root.set("timestamp", ((Date) obj).getTime());
}
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);
@ -168,6 +175,7 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput
}
}
}
}
return root;
}

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