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     */
011    package com.thoughtworks.proxy.toys.nullobject;
012    
013    import java.io.IOException;
014    import java.io.NotSerializableException;
015    import java.io.ObjectOutputStream;
016    import java.io.Serializable;
017    import java.lang.reflect.Method;
018    
019    import com.thoughtworks.proxy.Invoker;
020    import com.thoughtworks.proxy.ProxyFactory;
021    import 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     */
030    public 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    }