/*
 * 
 * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * The contents of this file are subject to the Sun Community Source
 * License v3.0/Jini Technology Specific Attachment v 1.0 (the
 * "License"). You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 * http://wwws.sun.com/software/jini/licensing/SCSL3_JiniTSA1.html.
 * 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.
 * 
 * The Reference Code is Jini Technology Core Platform code, v2.0. The
 * Developer of the Reference Code is Sun Microsystems, Inc.
 * 
 * Contributor(s): Sun Microsystems, Inc.
 * 
 * The contents of this file comply with the Jini Technology Core
 * Platform Compatibility Kit, v2.0A.
 * 
 * Tester(s): Sun Microsystems, Inc.
 * 
 * Test Platform(s):
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Solaris(TM)SPARC/x86
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Linux (Intel x86)
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Windows (Intel Platform)
 * 
 */

package net.jini.io;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.rmi.server.RMIClassLoader;
import java.util.Collection;

/**
 * An extension of <code>ObjectOutputStream</code> that implements the
 * dynamic class loading semantics of RMI argument and result
 * marshalling (using {@link RMIClassLoader}).  A
 * <code>MarshalOutputStream</code> writes data that is intended to be
 * written by a corresponding {@link MarshalInputStream}.
 *
 * <p><code>MarshalOutputStream</code> implements the output side of
 * the dynamic class loading semantics by overriding {@link
 * ObjectOutputStream#annotateClass annotateClass} and {@link
 * ObjectOutputStream#annotateProxyClass annotateProxyClass} to
 * annotate class descriptors in the stream with codebase strings
 * obtained using {@link RMIClassLoader#getClassAnnotation
 * RMIClassLoader.getClassAnnotation}.
 *
 * <p><code>MarshalOutputStream</code> writes class annotations to its
 * own stream; a subclass may override the {@link #writeAnnotation
 * writeAnnotation} method to write the class annotations to a
 * different location.
 *
 * <p><code>MarshalOutputStream</code> does not modify the stream
 * protocol version of its instances' superclass state (see {@link
 * ObjectOutputStream#useProtocolVersion
 * ObjectOutputStream.useProtocolVersion}).
 *
 * @author Sun Microsystems, Inc.
 * @since 2.0
 **/
public class MarshalOutputStream
    extends ObjectOutputStream
    implements ObjectStreamContext
{
    /** context for ObjectStreamContext implementation */
    private final Collection context;

    /**
     * Creates a new <code>MarshalOutputStream</code> that writes
     * marshalled data to the specified underlying
     * <code>OutputStream</code>.
     *
     * <p>This constructor passes <code>out</code> to the superclass
     * constructor that has an <code>OutputStream</code> parameter.
     *
     * <p><code>context</code> will be used as the return value of the
     * created stream's {@link #getObjectStreamContext
     * getObjectStreamContext} method.
     *
     * @param out the output stream to write marshalled data to
     *
     * @param context the collection of context information objects to
     * be returned by this stream's {@link #getObjectStreamContext
     * getObjectStreamContext} method
     *
     * @throws IOException if the superclass's constructor throws an
     * <code>IOException</code>
     *
     * @throws SecurityException if the superclass's constructor
     * throws a <code>SecurityException</code>
     *
     * @throws NullPointerException if <code>out</code> or
     * <code>context</code> is <code>null</code>
     **/
    public MarshalOutputStream(OutputStream out, Collection context)
	throws IOException
    {
	super(out);
	if (context == null) {
	    throw new NullPointerException();
	}
	this.context = context;
    }

    /**
     * Returns the collection of context information objects that
     * was passed to this stream's constructor.
     **/
    public Collection getObjectStreamContext() {
	return context;
    }

    /**
     * Annotates the stream descriptor for the class <code>cl</code>.
     *
     * <p><code>MarshalOutputStream</code> implements this method as
     * follows:
     *
     * <p>This method invokes {@link RMIClassLoader#getClassAnnotation
     * RMIClassLoader.getClassAnnotation} with <code>cl</code> to get
     * the appropriate class annotation string value (possibly
     * <code>null</code>), and then it invokes this stream's {@link
     * #writeAnnotation writeAnnotation} method with that string to
     * record the annotation.
     *
     * @param cl the class to annotate
     *
     * @throws IOException if <code>writeAnnotation</code> throws an
     * <code>IOException</code>
     *
     * @throws NullPointerException if <code>cl</code> is
     * <code>null</code>
     **/
    protected void annotateClass(Class cl) throws IOException {
	writeAnnotation(RMIClassLoader.getClassAnnotation(cl));
    }

    /**
     * Annotates the stream descriptor for the proxy class
     * <code>cl</code>.
     *
     * <p><code>MarshalOutputStream</code> implements this method as
     * follows:
     *
     * <p>This method invokes {@link RMIClassLoader#getClassAnnotation
     * RMIClassLoader.getClassAnnotation} with <code>cl</code> to get
     * the appropriate class annotation string value (possibly
     * <code>null</code>), and then it invokes this stream's {@link
     * #writeAnnotation writeAnnotation} method with that string to
     * record the annotation.
     *
     * @param cl the proxy class to annotate
     *
     * @throws IOException if <code>writeAnnotation</code> throws an
     * <code>IOException</code>
     *
     * @throws NullPointerException if <code>cl</code> is
     * <code>null</code>
     **/
    protected void annotateProxyClass(Class cl) throws IOException {
	writeAnnotation(RMIClassLoader.getClassAnnotation(cl));
    }

    /**
     * Writes a class annotation string value (possibly
     * <code>null</code>) to be read by a corresponding
     * <code>MarshalInputStream</code> implementation.
     *
     * <p><code>MarshalOutputStream</code> implements this method to
     * just write the annotation value to this stream using {@link
     * ObjectOutputStream#writeObject writeObject}.
     *
     * <p>A subclass can override this method to write the annotation
     * to a different location.
     *
     * @param annotation the class annotation string value (possibly
     * <code>null</code>) to write
     *
     * @throws IOException if I/O exception occurs writing the
     * annotation
     **/
    protected void writeAnnotation(String annotation) throws IOException {
	writeObject(annotation);
    }
}
