Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion devkit-core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion devkit-utils/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024-2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
202 changes: 48 additions & 154 deletions devkit-utils/src/main/java/com/onixbyte/devkit/utils/MapUtil.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
* Copyright (C) 2024 OnixByte.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,204 +19,98 @@

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
* {@code MapUtil} is a utility class that provides methods for converting
* objects to maps and maps to objects.
* {@code MapUtil} is a utility class that provides methods for converting objects to maps and maps
* to objects.
* <p>
* It also provides methods for getting and setting field values using
* reflection.
* Note: Since version 1.4.2, this util class removed reflection API and transferred to a safer API.
* Please see <a href="">documentation</a> for more information.
*
* @author Zihlu Wang
* @version 1.1.0
* @version 1.4.2
* @see com.onixbyte.devkit.utils.unsafe.ReflectMapUtil
* @since 1.0.0
*/
@Slf4j
public final class MapUtil {

/**
* Converts an object to a map by mapping the field names to their
* corresponding values.
* Converts an object to a map by mapping the field names to their corresponding values.
*
* @param obj the object to be converted to a map
* @param entity the object to be converted to a map
* @return a map representing the fields and their values of the object
* @throws IllegalAccessException if an error occurs while accessing the
* fields of the object
*/
public static Map<String, Object> objectToMap(Object obj) throws IllegalAccessException {
if (obj == null) {
return null;
}

var map = new HashMap<String, Object>();

var declaredFields = obj.getClass().getDeclaredFields();
for (var field : declaredFields) {
field.setAccessible(true);
Object result = field.get(obj);
if (result != null) {
map.put(field.getName(), result);
}
}

return map;
public static <T> Map<String, Object> objectToMap(T entity,
Map<String, ObjectMapAdapter<T, ?>> adapters) {
var resultMap = new HashMap<String, Object>();
adapters.forEach((fieldName, adapter) -> resultMap.put(fieldName, adapter.fetch(entity)));
return resultMap;
}

/**
* Converts a map to an object of the specified type by setting the field
* values using the map entries.
* Converts a map to an object of the specified type by setting the field values using the
* map entries.
*
* @param map the map representing the fields and their values
* @param requiredType the class of the object to be created
* @param <T> the type of the object to be created
* @return an object of the specified type with the field values set from
* the map
* @throws NoSuchMethodException if the constructor of the required
* type is not found
* @throws InvocationTargetException if an error occurs while invoking the
* constructor
* @throws InstantiationException if the required type is abstract or an
* interface
* @throws IllegalAccessException if an error occurs while accessing the
* fields of the object
* @param objectMap the map representing the fields and their values
* @param entity an empty entity of the target class
* @param adapters the adapters to execute the setter for the entity
* @param <T> the type of the object to be created
* @return an object of the specified type with the field values set from the map
*/
public static <T> T mapToObject(Map<String, Object> map, Class<T> requiredType) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
var bean = requiredType.getConstructor().newInstance();
if (map != null) {
for (var entry : map.entrySet()) {
try {
var entryValue = entry.getValue().toString();
// get the field by field name
var field = requiredType.getDeclaredField(entry.getKey());
var fieldType = field.getGenericType();

// convert field value by class
if (fieldType instanceof Class<?> fieldClass) {
if (fieldClass == Short.class || fieldClass == short.class) {
entry.setValue(Short.parseShort(entryValue));
} else if (fieldClass == Integer.class || fieldClass == int.class) {
entry.setValue(Integer.parseInt(entryValue));
} else if (fieldClass == Long.class || fieldClass == long.class) {
entry.setValue(Long.parseLong(entryValue));
} else if (fieldClass == Float.class || fieldClass == float.class) {
entry.setValue(Float.parseFloat(entryValue));
} else if (fieldClass == Double.class || fieldClass == double.class) {
entry.setValue(Double.parseDouble(entryValue));
} else if (fieldClass == Character.class || fieldClass == char.class) {
entry.setValue(entryValue.charAt(0));
} else if (fieldClass == Byte.class || fieldClass == byte.class) {
entry.setValue(Byte.parseByte(entryValue));
} else if (fieldClass == Boolean.class || fieldClass == boolean.class) {
entry.setValue(Boolean.parseBoolean(entryValue));
} else if (fieldClass == String.class) {
entry.setValue(entryValue);
} else {
log.error("Unable to determine the type of property {}.", field.getName());
continue;
}
}

setFieldValue(bean, entry.getKey(), entry.getValue());
} catch (Exception e) {
log.error("Map to Object failed.");
}
}
}
return bean;
public static <T> T mapToObject(Map<String, Object> objectMap,
T entity,
Map<String, ObjectMapAdapter<T, ?>> adapters) {
adapters.forEach((fieldName, adapter) -> Optional.ofNullable(objectMap)
.map((data) -> data.get(fieldName))
.ifPresent((fieldValue) -> adapter.setValue(entity, fieldValue)));
return entity;
}

/**
* Retrieves the value of a field from an object using reflection.
*
* @param obj the object from which to retrieve the field value
* @param fieldName the name of the field
* @param fieldType the class representing the type of the field value
* @param <T> the type of the field value
* @return the value of the field in the object, or null if the field does
* not exist or cannot be accessed
* @throws IllegalAccessException if an error occurs while accessing the
* field
* @throws InvocationTargetException if an error occurs while invoking the
* field getter method
* @throws NoSuchMethodException if the specified getter is not present
* @param <T> the type of the field value
* @param entity the object from which to retrieve the field value
* @param adapter the adapter to execute the getter
* @return the value of the field in the object, or null if the field does not exist or cannot
* be accessed
*/
public static <T> T getFieldValue(Object obj, String fieldName, Class<T> fieldType) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
var methodName = getMethodName("get", fieldName);
var objectClass = obj.getClass();
var method = objectClass.getDeclaredMethod(methodName);

method.setAccessible(true);
return cast(method.invoke(obj), fieldType);
public static <E, T> T getFieldValue(E entity, ObjectMapAdapter<E, T> adapter) {
return adapter.fetch(entity);
}

/**
* Sets the value of a field in an object using reflection.
*
* @param obj the object in which to set the field value
* @param fieldName the name of the field
* @param entity the object in which to set the field value
* @param adapter the adapter to execute the setter
* @param fieldValue the value to be set
* @throws InvocationTargetException if an error occurs while invoking the
* field setter method
* @throws IllegalAccessException if an error occurs while accessing the
* field
* @throws NoSuchMethodException if the specific setter is not present
*/
public static void setFieldValue(Object obj, String fieldName, Object fieldValue) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
var objectClass = obj.getClass();
var methodName = getMethodName("set", fieldName);
var method = objectClass.getDeclaredMethod(methodName, fieldValue.getClass());
method.setAccessible(true);
method.invoke(obj, fieldValue);
public static <E, T> void setFieldValue(E entity,
ObjectMapAdapter<E, T> adapter,
Object fieldValue) {
adapter.setValue(entity, fieldValue);
}

/**
* Casts the specified value to the required type.
* Casts the specified value to the required type with Optional.
*
* @param value the value to be cast
* @param requiredType the type to which the value should be cast
* @param <T> the type to which the value should be cast
* @return the cast value, or null if the value cannot be cast to the
* required type
* @return the cast value, or {@code null} if the value is not an instance of the requiredType
*/
public static <T> T cast(Object value, Class<T> requiredType) {
if (requiredType.isInstance(value)) {
return requiredType.cast(value);
}
return null;
}

/**
* Constructs a method name based on the given prefix and field name.
*
* @param prefix the prefix to be added to the field name
* @param fieldName the name of the field
* @return the constructed method name
*/
private static String getMethodName(String prefix, String fieldName) {
return prefix + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
return Optional.ofNullable(requiredType)
.filter((clazz) -> clazz.isInstance(value))
.map((clazz) -> clazz.cast(value))
.orElse(null);
}

/**
* Returns the default string representation of the specified object.
*
* @param obj the object for which to return the default string
* representation
* @return the default string representation of the object
*/
private static String defaultObject(Object obj) {
if (obj == null) {
return "";
} else {
return String.valueOf(obj);
}
}

/**
* Private constructor will protect this class from being instantiated.
*/
private MapUtil() {
}
}
Loading