com.sap.cds.impl.ProxyCreator Maven / Gradle / Ivy
/*******************************************************************
* © 2019 SAP SE or an SAP affiliate company. All rights reserved. *
*******************************************************************/
package com.sap.cds.impl;
import static java.util.Arrays.stream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import com.google.common.collect.ImmutableMap;
import com.sap.cds.CdsException;
import com.sap.cds.JSONizable;
import com.sap.cds.Row;
import com.sap.cds.Struct.ProxyFactory;
import com.sap.cds.impl.builder.model.StructuredTypeProxy;
import com.sap.cds.impl.parser.token.Jsonizer;
import com.sap.cds.ql.CdsName;
import com.sap.cds.ql.StructuredType;
public class ProxyCreator implements ProxyFactory {
private static final ConcurrentMap GENERIC_RETURN_TYPE = new ConcurrentHashMap<>();
private static final ConcurrentMap, ImmutableMap> CLASS_MAPPING_RETURN_TYPE = new ConcurrentHashMap<>();
private static final Method JSONIZABLE_TO_JSON;
private static final Method EQUALS;
static {
try {
JSONIZABLE_TO_JSON = JSONizable.class.getDeclaredMethod("toJson");
EQUALS = Object.class.getDeclaredMethod("equals", Object.class);
} catch (NoSuchMethodException | SecurityException e) {
throw new AssertionError("JSONizable::toJson must exist", e); // NOSONAR
}
}
private static V createIfAbsentPreferGet(ConcurrentMap map, K key,
Function super K, ? extends V> provider) {
V res = map.get(key); // do not use computeIfAbsent because ConcurrentHashMap may lock.
if (res == null) {
V r = provider.apply(key);
res = map.putIfAbsent(key, r);
if (res == null) {
res = r;
}
}
return res;
}
private static Type getGenericReturnType(Method method) {
return createIfAbsentPreferGet(GENERIC_RETURN_TYPE, method, (m) -> m.getGenericReturnType());
}
@Override
public T proxy(Map data, Class proxyInterface) {
return createProxy(data, proxyInterface);
}
static T createProxy(Map data, Class proxyInterface) {
return createProxy(data, proxyInterface,
createIfAbsentPreferGet(CLASS_MAPPING_RETURN_TYPE, proxyInterface, ProxyCreator::createMapping));
}
@SuppressWarnings("unchecked")
static T createProxy(Map data, Class proxyInterface, Map mapping) {
Row row = data instanceof Row ? (Row) data : RowImpl.row(data);
return (T) Proxy.newProxyInstance(MapProxy.class.getClassLoader(), new Class[] { proxyInterface },
new MapProxy(row, mapping));
}
static ImmutableMap createMapping(Class> type) {
ImmutableMap.Builder delegates = stream(type.getDeclaredMethods()).collect(
ImmutableMap::builder,
(BiConsumer, ? super Method>) (builder, method) -> {
Delegate delegate = delegate(method);
if (delegate != null) {
builder.put(method, delegate);
}
}, (b1, b2) -> b1.putAll(b2.build()));
registerToJson(type, delegates);
registerEquals(delegates);
return delegates.build();
}
private static void registerToJson(Class> type, ImmutableMap.Builder delegates) {
if (JSONizable.class.isAssignableFrom(type)) {
delegates.put(JSONIZABLE_TO_JSON, (data, a, p) -> Jsonizer.json(data));
}
}
private static void registerEquals(ImmutableMap.Builder delegates) {
delegates.put(EQUALS, (data, a, p) -> {
Object other = a[0];
if (p == other) {
return true;
}
return data.equals(other);
});
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static Delegate delegate(Method method) {
CdsName nameAnnotation = method.getAnnotation(CdsName.class);
String cdsName = nameAnnotation != null ? nameAnnotation.value() : propertyName(method);
if ("ref".equals(method.getName()) && method.getParameterCount() == 0) {
return (row, a, p) -> {
Class type = (Class) method.getReturnType();
return StructuredTypeProxy.create(row.ref(), type);
};
}
if (isGetter(method)) {
Class> type = method.getReturnType();
if (Map.class.isAssignableFrom(type) && Map.class != type) {
return proxyMap(cdsName, type);
}
if (Collection.class.isAssignableFrom(type)) {
return proxyList(method, cdsName);
}
return (data, a, p) -> data.get(cdsName);
}
if (isSetter(method)) {
return (data, args, proxy) -> {
data.put(cdsName, args[0]);
return proxy;
};
}
return null;
}
@SuppressWarnings("unchecked")
private static Delegate proxyMap(String cdsName, Class> type) {
return (data, a, p) -> {
Object value = data.get(cdsName);
if (value == null || value instanceof Proxy) {
return value;
}
return createProxy((Map) value, type);
};
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static Delegate proxyList(Method method, String cdsName) {
Type returnType = getGenericReturnType(method);
if (returnType instanceof ParameterizedType) {
Type type = ((ParameterizedType) returnType).getActualTypeArguments()[0];
if (type instanceof Class && Map.class.isAssignableFrom((Class) type)) {
return (data, a, p) -> {
Object list = data.get(cdsName);
return list == null ? null : new ProxyList((List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy