All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cantaa.util.wicket.SafeModel Maven / Gradle / Ivy

There is a newer version: 2.0
Show newest version
package com.cantaa.util.wicket;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cantaa.util.StringUtil;

/**
 * To create type safe bean string-paths and wicket Property-Models
 * @author Hans Lesmeister
 */
public class SafeModel {
    private static final Logger log = LoggerFactory.getLogger(SafeModel.class);

    private static final ThreadLocal> PATH = new ThreadLocal>();
    private static final ThreadLocal TARGET = new ThreadLocal();
    private static final ThreadLocal> LASTTYPE = new ThreadLocal>();

    private static final Map, Object> typeMap;


    private static final NullType nullType = new NullType();

    private static final class NullType {
    }

    static {
        typeMap = new HashMap, Object>();
        typeMap.put(boolean.class, Boolean.FALSE);
        typeMap.put(int.class, Integer.valueOf(0));
        typeMap.put(long.class, Long.valueOf(0));
        typeMap.put(BigDecimal.class, nullType);
        typeMap.put(Timestamp.class, nullType);
        typeMap.put(List.class, nullType);
    }


    @SuppressWarnings("unchecked")
    public static  T from(T object) {
        if (object == null) {
            throw new IllegalArgumentException("object is null");
        }

        setTarget(object);
        return (T) from(object.getClass());
    }

    public static  T from(Class clazz) {
        if (clazz == null) {
            throw new IllegalArgumentException("clazz is null");
        }

        T proxy = createProxy(clazz);
        return proxy;
    }

    public static  IModel model(@SuppressWarnings("UnusedParameters") K proxy) {
        PropertyModel propertyModel = new PropertyModel(TARGET.get(), buildPath());
        clearPath();
        return propertyModel;
    }

    public static  IModel model(IModel model, @SuppressWarnings("UnusedParameters") K proxy) {
        PropertyModel propertyModel = new PropertyModel(model, buildPath());
        clearPath();
        return propertyModel;
    }

    public static String path(@SuppressWarnings("UnusedParameters") Object proxy) {
        String path = buildPath();
        clearPath();
        return path;
    }

    private static  void setLastType(Class lastType) {
        LASTTYPE.set(lastType);
    }

    private static  void setTarget(T object) {
        TARGET.set(object);
    }

    private static void clearPath() {
        List list = PATH.get();
        if (list != null) {
            list.clear();
        }
        PATH.remove();
    }

    private static void addToPath(String propName) {
        List list = PATH.get();
        if (list == null) {
            list = new ArrayList();
            PATH.set(list);
        }

        list.add(propName);
    }

    @SuppressWarnings("unchecked")
    private static  T createProxy(Class clazz) {
        ProxyFactory factory = new ProxyFactory();
        if (clazz.isInterface()) {
            factory.setInterfaces(new Class[] {clazz});
        }
        else {
            factory.setSuperclass(clazz);
        }

        factory.setFilter(
                new MethodFilter() {
                    @Override
                    public boolean isHandled(Method method) {
                        String methodName = method.getName();

                        if (methodName.startsWith("get")) {
                            return true;
                        }

                        if (methodName.startsWith("is")) {
                            return true;
                        }

                        return false;
                    }
                }
        );//      return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, HANDLER);

        MethodHandler handler = new MethodHandler() {
            @Override
            public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
                String methodName = thisMethod.getName();
                int pos = methodName.startsWith("get") ? 3 : 2; // getXXX or isXXX
                String propName = StringUtil.lowerFirstCharacter(methodName.substring(pos));
                addToPath(propName);
                Class methodType = (Class) thisMethod.getReturnType();
                setLastType(methodType);

                if (methodType.isPrimitive()) {
                    Object retVal = mapType(methodType);
                    if (retVal == null) {
                        throw new RuntimeException("Primitive type " + methodType + " is not supported");
                    }
                    return retVal;
                }

                if (Modifier.isFinal(methodType.getModifiers())) {
                    return null;
                }

                log.debug("MethodType to analyse: {}", methodType);
                Object retVal = mapType(methodType);
                if (retVal == null) {
                    log.debug("Mapping returned null, so create proxy");
                    return createProxy(methodType);
                }

                if (retVal instanceof NullType) {
                    log.debug("Mapping returned NullType");
                    return null;
                }
                else {
                    log.debug("Mapping returned: {}", retVal);
                    return retVal;
                }

            }
        };

        try {
            return (T) factory.create(new Class[0], new Object[0], handler);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static  Object mapType(Class type) {
        return typeMap.get(type);
    }

    private static String buildPath() {
        List list = PATH.get();
        if (list == null) {
            throw new IllegalStateException("No current path available");
        }

        StringBuilder b = new StringBuilder();
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);

            if (i > 0) {
                b.append(".");
            }

            b.append(s);
        }

        return b.toString();
    }
}