/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later,
 * or the Apache License Version 2.0.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */

package javassist.util.proxy;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;

/**
 * An input stream class which knows how to deserialize proxies created via {@link ProxyFactory} and
 * serializedo via a {@link ProxyObjectOutputStream}. It must be used when deserialising proxies created
 * from a proxy factory configured with {@link ProxyFactory#useWriteReplace} set to false.
 *
 * @author Andrew Dinn
 */
public class ProxyObjectInputStream extends ObjectInputStream
{
    /**
     * create an input stream which can be used to deserialize an object graph which includes proxies created
     * using class ProxyFactory. the classloader used to resolve proxy superclass and interface names
     * read from the input stream will default to the current thread's context class loader or the system
     * classloader if the context class loader is null.
     * @param in
     * @throws java.io.StreamCorruptedException whenever ObjectInputStream would also do so
     * @throws	IOException whenever ObjectInputStream would also do so
     * @throws	SecurityException whenever ObjectInputStream would also do so
     * @throws NullPointerException if in is null
     */
    public ProxyObjectInputStream(InputStream in) throws IOException
    {
        super(in);
        loader = Thread.currentThread().getContextClassLoader();
        if (loader == null) {
            loader = ClassLoader.getSystemClassLoader();
        }
    }

    /**
     * Reset the loader to be
     * @param loader
     */
    public void setClassLoader(ClassLoader loader)
    {
        if (loader != null) {
            this.loader = loader;
        } else {
            loader = ClassLoader.getSystemClassLoader();
        }
    }

    @Override
    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
        boolean isProxy = readBoolean();
        if (isProxy) {
            String name = (String)readObject();
            Class<?> superClass = loader.loadClass(name);
            int length = readInt();
            Class<?>[] interfaces = new Class[length];
            for (int i = 0; i < length; i++) {
                name = (String)readObject();
                interfaces[i] = loader.loadClass(name);
            }
            length = readInt();
            byte[] signature = new byte[length];
            read(signature);
            ProxyFactory factory = new ProxyFactory();
            // we must always use the cache and never use writeReplace when using
            // ProxyObjectOutputStream and ProxyObjectInputStream
            factory.setUseCache(true);
            factory.setUseWriteReplace(false);
            factory.setSuperclass(superClass);
            factory.setInterfaces(interfaces);
            Class<?> proxyClass = factory.createClass(signature);
            return ObjectStreamClass.lookup(proxyClass);
        }
        return super.readClassDescriptor();
    }

    /**
     * the loader to use to resolve classes for proxy superclass and interface names read
     * from the stream. defaults to the context class loader of the thread which creates
     * the input stream or the system class loader if the context class loader is null.
     */
    private ClassLoader loader;
}
