001/*
002 * (c) 2003-2005, 2009, 2010 ThoughtWorks Ltd
003 * All rights reserved.
004 *
005 * The software in this package is published under the terms of the BSD
006 * style license a copy of which has been included with this distribution in
007 * the LICENSE.txt file.
008 * 
009 * Created on 14-May-2004
010 */
011package com.thoughtworks.proxy.toys.nullobject;
012
013import java.io.IOException;
014import java.io.NotSerializableException;
015import java.io.ObjectOutputStream;
016import java.io.Serializable;
017import java.lang.reflect.Method;
018
019import com.thoughtworks.proxy.Invoker;
020import com.thoughtworks.proxy.ProxyFactory;
021import com.thoughtworks.proxy.kit.ReflectionUtils;
022
023
024/**
025 * A {@link Invoker} implementation that returns always new Null objects.
026 *
027 * @author Dan North
028 * @since 0.1
029 */
030public class NullInvoker implements Invoker {
031    private static final long serialVersionUID = -4713875509846468548L;
032    private static final Method toString;
033
034    static {
035        try {
036            toString = Object.class.getMethod("toString", new Class[0]);
037        } catch (NoSuchMethodException e) {
038            throw new ExceptionInInitializerError(e.toString());
039        }
040    }
041
042    private Class<?> type;
043    private ProxyFactory proxyFactory;
044
045    /**
046     * Construct a NullInvoker.
047     *
048     * @param type         the type of the proxy
049     * @param proxyFactory the {@link ProxyFactory} to use
050     * @since 0.1
051     */
052    public NullInvoker(final Class<?> type, final ProxyFactory proxyFactory) {
053        this.type = type;
054        this.proxyFactory = proxyFactory;
055    }
056
057    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
058        Object result;
059
060        // Object methods
061        if (toString.equals(method)) {
062            result = "Null Object for " + type.getName();
063        } else if (ReflectionUtils.equals.equals(method)) {
064            Object other = args[0];
065            result = (Null.isNullObject(other, proxyFactory) && type.equals(getType(other)));
066        } else if (ReflectionUtils.hashCode.equals(method)) {
067            result = type.hashCode();
068        }
069
070        // Just another null object
071        else {
072            result = Null.proxy(method.getReturnType()).build(proxyFactory);
073        }
074        return result;
075    }
076
077    private Class<?> getType(Object object) {
078        final Class<?> result;
079        if (proxyFactory.isProxyClass(object.getClass())) {
080            NullInvoker nullInvoker = NullInvoker.class.cast(proxyFactory.getInvoker(object));
081            result = nullInvoker.type;
082        } else {
083            result = object.getClass();
084        }
085        return result;
086    }
087
088    // Serialization
089
090    private void writeObject(ObjectOutputStream out) throws IOException {
091        if (!nullObjectIsSerializable()) {
092            throw new NotSerializableException(type.getName());
093        }
094        out.defaultWriteObject();
095    }
096
097    private boolean nullObjectIsSerializable() {
098        return Serializable.class.isAssignableFrom(type);
099    }
100}