Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.rapidoid.beany.Beany Maven / Gradle / Ivy
/*-
* #%L
* rapidoid-commons
* %%
* Copyright (C) 2014 - 2018 Nikolche Mihajlovski and contributors
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.rapidoid.beany;
import org.rapidoid.RapidoidThing;
import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.annotation.ToString;
import org.rapidoid.annotation.Transient;
import org.rapidoid.cls.Cls;
import org.rapidoid.cls.TypeKind;
import org.rapidoid.collection.Coll;
import org.rapidoid.commons.Dates;
import org.rapidoid.lambda.Mapper;
import org.rapidoid.log.Log;
import org.rapidoid.u.U;
import org.rapidoid.var.Var;
import org.rapidoid.var.Vars;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.Map.Entry;
@Authors("Nikolche Mihajlovski")
@Since("2.0.0")
public class Beany extends RapidoidThing {
private static final String GETTER = "^(get|is)[A-Z].*";
private static final String SETTER = "^set[A-Z].*";
protected static final Map, BeanProperties> BEAN_PROPERTIES = Coll
.autoExpandingMap(new Mapper, BeanProperties>() {
@Override
public BeanProperties map(Class> clazz) throws Exception {
Map properties = new LinkedHashMap();
getBeanProperties(clazz, properties);
for (Entry e : properties.entrySet()) {
e.getValue().init();
}
return new BeanProperties(properties);
}
});
private static void getBeanProperties(Class> clazz, Map properties) {
if (clazz == null || clazz == Object.class) {
return;
} else {
getBeanProperties(clazz.getSuperclass(), properties);
for (Class> interf : clazz.getInterfaces()) {
getBeanProperties(interf, properties);
}
}
if (Proxy.isProxyClass(clazz)) {
return;
}
try {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
int modif = method.getModifiers();
Class>[] params = method.getParameterTypes();
Class> ret = method.getReturnType();
String name = method.getName();
if (!name.startsWith("_") && !name.contains("$")
&& !Modifier.isPrivate(modif) && !Modifier.isProtected(modif) && !Modifier.isStatic(modif)
&& !method.isAnnotationPresent(Transient.class)) {
if ((name.matches(GETTER) && params.length == 0) || (name.matches(SETTER) && params.length == 1)) {
String propName;
if (name.startsWith("is")) {
propName = name.substring(2, 3).toLowerCase() + name.substring(3);
} else {
propName = name.substring(3, 4).toLowerCase() + name.substring(4);
}
BeanProp prop = properties.get(propName);
if (prop == null) {
prop = new BeanProp(propName);
properties.put(propName, prop);
}
if (name.startsWith("set")) {
prop.setSetter(method);
prop.setReadOnly(false);
} else {
prop.setGetter(method);
}
} else if (!name.matches("^to[A-Z].*") && !name.equals("hashCode")) {
if (params.length == 0 && !ret.equals(void.class)) {
String propName = name;
BeanProp prop = properties.get(propName);
if (prop == null) {
prop = new BeanProp(propName);
properties.put(propName, prop);
}
prop.setGetter(method);
} else if (params.length == 1) {
String propName = name;
BeanProp prop = properties.get(propName);
if (prop == null) {
prop = new BeanProp(propName);
properties.put(propName, prop);
}
prop.setReadOnly(false);
prop.setSetter(method);
}
}
}
}
// remove properties with setters, without getters
for (Iterator> it = properties.entrySet().iterator(); it.hasNext(); ) {
Entry entry = it.next();
BeanProp minfo = entry.getValue();
if (minfo.getGetter() == null && minfo.getSetter() != null) {
it.remove();
}
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
int modif = field.getModifiers();
if (!Modifier.isPrivate(modif) && !Modifier.isProtected(modif) && !Modifier.isStatic(modif)) {
String fieldName = field.getName();
if (!fieldName.startsWith("_") && !fieldName.contains("$")
&& !field.isAnnotationPresent(Transient.class)) {
BeanProp prop = properties.get(fieldName);
if (prop == null) {
prop = new BeanProp(fieldName, field, (modif & Modifier.FINAL) != 0);
properties.put(fieldName, prop);
}
}
}
}
} catch (Exception e) {
throw U.rte(e);
}
}
public static void reset() {
BEAN_PROPERTIES.clear();
}
public static BeanProperties propertiesOf(Class> clazz) {
return BEAN_PROPERTIES.get(clazz);
}
@SuppressWarnings({"rawtypes", "unchecked"})
public static BeanProperties propertiesOf(Object obj) {
if (obj == null) {
return BeanProperties.NONE;
} else if (obj instanceof Map) {
return BeanProperties.from(((Map) obj));
} else if (obj instanceof Class) {
return propertiesOf((Class) obj);
} else {
return propertiesOf(obj.getClass());
}
}
public static Prop property(Class> clazz, String property, boolean mandatory) {
Prop prop = BEAN_PROPERTIES.get(clazz).get(property);
if (prop == null && JSProp.is(property)) {
prop = new JSProp(property);
}
if (prop == null && ActionsProp.is(property)) {
prop = new ActionsProp();
}
if (mandatory && prop == null) {
throw U.rte("Cannot find the property '%s' in the class '%s'", property, clazz);
}
return prop;
}
public static Object serialize(Object value) {
if (Cls.kindOf(value) != TypeKind.UNKNOWN || value instanceof Enum) {
return value;
} else if (value instanceof Var>) {
Var> var = (Var>) value;
return serialize(U.map(var.name(), var.get()));
} else if (value instanceof Set) {
Set set = U.set();
for (Object item : ((Set>) value)) {
set.add(serialize(item));
}
return set;
} else if (value instanceof List) {
List list = U.list();
for (Object item : ((List>) value)) {
list.add(serialize(item));
}
return list;
} else if (value instanceof Object[]) {
Object[] vals = (Object[]) value;
Object[] arr = new Object[vals.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = serialize(vals[i]);
}
return arr;
} else if (value instanceof Map) {
Map map = U.map();
for (Entry, ?> e : ((Map, ?>) value).entrySet()) {
map.put(serialize(e.getKey()), serialize(e.getValue()));
}
return map;
} else if (value.getClass().isArray()) {
// a primitive array
return value;
} else {
return read(value);
}
}
public static Map read(Object bean) {
Map props = U.map();
read(bean, props);
return props;
}
public static void read(Object bean, Map dest) {
U.notNull(bean, "bean");
U.must(!(bean instanceof Collection));
U.must(!bean.getClass().isArray());
U.must(!bean.getClass().isEnum());
U.must(Cls.kindOf(bean) == TypeKind.UNKNOWN);
for (Prop prop : propertiesOf(bean)) {
Object value = prop.getRaw(bean);
if (value instanceof SerializableBean>) {
SerializableBean> ser = (SerializableBean>) value;
value = ser.serializeBean();
} else {
value = serialize(unwrap(value));
}
dest.put(prop.getName(), value);
}
}
public static void bind(Object destBean, Map src) {
Method bind = Cls.findMethod(destBean.getClass(), "bind", Map.class);
if (bind != null) {
Cls.invoke(bind, destBean, src);
} else {
update(destBean, src, false, false);
}
}
public static void update(Object destBean, Map src) {
update(destBean, src, false, false);
}
public static void update(Object destBean, Map src, boolean ignoreNullValues) {
update(destBean, src, ignoreNullValues, false);
}
@SuppressWarnings("unchecked")
public static void update(Object destBean, Map src, boolean ignoreNullValues,
boolean ignoreReadOnlyProperties) {
if (destBean instanceof Map) {
((Map) destBean).putAll(src);
return;
}
for (Prop prop : propertiesOf(destBean)) {
Object value = src.get(prop.getName());
if (ignoreReadOnlyProperties && prop.isReadOnly()) {
continue;
}
if (value == null) {
// differentiate non-existing entries from existing entries with null value
if (ignoreNullValues || !src.containsKey(prop.getName())) {
continue;
}
}
Object propValue = prop.getRaw(destBean);
if (propValue != null && propValue instanceof SerializableBean) {
SerializableBean ser = (SerializableBean) propValue;
ser.deserializeBean(value);
} else {
prop.set(destBean, value);
}
}
}
@SuppressWarnings("rawtypes")
public static Prop property(Object obj, String property, boolean mandatory) {
if (obj instanceof Map) {
Object value = ((Map) obj).get(property);
return new MapProp(property, property, Cls.of(value));
} else {
return property(obj.getClass(), property, mandatory);
}
}
public static boolean hasProperty(Class> clazz, String property) {
return property(clazz, property, false) != null;
}
public static boolean hasProperty(Object obj, String property) {
return property(obj, property, false) != null;
}
@SuppressWarnings("unchecked")
public static void setPropValue(Object instance, String propertyName, Object value) {
if (instance instanceof Map) {
((Map) instance).put(propertyName, value);
} else {
property(instance, propertyName, true).set(instance, value);
}
}
@SuppressWarnings("unchecked")
public static T getPropValue(Object instance, String propertyName, T defaultValue) {
if (instance instanceof Map, ?>) {
Map, ?> map = (Map, ?>) instance;
return (T) (map.containsKey(propertyName) ? map.get(propertyName) : defaultValue);
} else {
Prop prop = property(instance, propertyName, false);
return prop != null ? (T) prop.get(instance) : defaultValue;
}
}
@SuppressWarnings("unchecked")
public static T getPropValue(Object instance, String propertyName) {
if (instance instanceof Map, ?>) {
Map, ?> map = (Map, ?>) instance;
U.must(map.containsKey(propertyName), "The map must contain key: %s", propertyName);
return (T) map.get(propertyName);
} else {
return property(instance.getClass(), propertyName, true).get(instance);
}
}
public static T getPropValueOfType(Object obj, String property, Class returnType) {
T value = getPropValueOfType(obj, property, returnType, null);
if (value == null) {
throw U.rte("The property '%s' cannot be null!", property);
}
return value;
}
public static T getPropValueOfType(Object obj, String property, Class returnType, T defaultValue) {
Object id = getPropValue(obj, property, null);
return id != null ? Cls.convert(id, returnType) : defaultValue;
}
public static void setId(Object obj, Object id) {
setPropValue(obj, "id", id);
}
public static void setId(Object obj, long id) {
setId(obj, "" + id);
}
public static Object getId(Object obj) {
return getPropValueOfType(obj, "id", Object.class);
}
public static long getLongId(Object obj) {
return getPropValueOfType(obj, "id", Long.class);
}
public static Object getIdIfExists(Object obj) {
return getPropValueOfType(obj, "id", Object.class, null);
}
public static Long getLongIdIfExists(Object obj) {
return getPropValueOfType(obj, "id", Long.class, null);
}
public static Object[] getIds(Object... objs) {
Object[] ids = new String[objs.length];
for (int i = 0; i < objs.length; i++) {
ids[i] = getId(objs[i]);
}
return ids;
}
public static String beanToStr(Object bean, boolean allowCustom) {
Class> clazz = Cls.unproxy(bean.getClass());
if (allowCustom) {
Method m = Cls.getMethod(clazz, "toString");
if (!m.getDeclaringClass().equals(Object.class)) {
return bean.toString();
}
}
BeanProperties props = propertiesOf(bean).annotated(ToString.class);
if (props.isEmpty()) {
Prop nameProp = property(bean, "name", false);
if (nameProp != null && nameProp.getType() == String.class) {
return U.safe((String) nameProp.get(bean));
}
props = propertiesOf(bean);
}
StringBuilder sb = new StringBuilder();
for (Prop prop : props) {
String name = prop.getName();
Object value = prop.get(bean);
if (sb.length() > 0) {
sb.append(", ");
}
if (prop.getTypeKind() == TypeKind.UNKNOWN) {
value = value == bean ? "[this]" : "[obj]";
}
if (value instanceof Date) {
Date date = (Date) value;
value = Dates.str(date);
}
sb.append(name);
sb.append(": ");
sb.append(value);
}
return sb.toString();
}
public static String beanToNiceText(Object bean, boolean allowCustom) {
Class> clazz = Cls.unproxy(bean.getClass());
if (allowCustom) {
Method m = Cls.getMethod(clazz, "toString");
if (!m.getDeclaringClass().equals(Object.class)) {
return bean.toString();
}
}
BeanProperties props = propertiesOf(bean).annotated(ToString.class);
if (props.isEmpty()) {
Prop nameProp = property(bean, "name", false);
if (nameProp != null && nameProp.getType() == String.class) {
return U.safe((String) nameProp.get(bean));
}
props = propertiesOf(bean);
}
StringBuilder sb = new StringBuilder();
for (Prop prop : props) {
String name = prop.getName();
Object value = prop.get(bean);
if (value != null && prop.getTypeKind() != TypeKind.UNKNOWN && prop.getTypeKind() != TypeKind.DATE) {
if (sb.length() > 0) {
sb.append(", ");
}
if (!(value instanceof String)) {
sb.append(name);
sb.append(": ");
}
sb.append(value);
}
}
return sb.toString();
}
public static Comparator comparator(final String orderBy) {
final int sign = orderBy.startsWith("-") ? -1 : 1;
final String order = sign == 1 ? orderBy : orderBy.substring(1);
Comparator comparator = new Comparator() {
@Override
public int compare(E o1, E o2) {
try {
E val1 = getPropValue(o1, order);
E val2 = getPropValue(o2, order);
U.must(val1 == null || val1 instanceof Comparable, "The property '%s' (%s) is not comparable!",
order, Cls.of((val1)));
U.must(val2 == null || val2 instanceof Comparable, "The property '%s' (%s) is not comparable!",
order, Cls.of((val2)));
return sign * U.compare(val1, val2);
} catch (Exception e) {
Log.error("Cannot compare values by: " + orderBy, e);
return 0;
}
}
};
return comparator;
}
@SuppressWarnings("unchecked")
public static List projection(Collection coll, String propertyName) {
List projection = U.list();
for (FROM item : coll) {
projection.add((TO) getPropValue(item, propertyName));
}
return projection;
}
public static Object unwrap(Object value) {
return Vars.unwrap(value);
}
}