//******************************************************************************
//
// File:    AbstractDoc.java
// Package: org.jpwg.jini.print.data
// Unit:    Class org.jpwg.jini.print.data.AbstractDoc
//
//******************************************************************************

package org.jpwg.jini.print.data;

import java.io.InputStream;
import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;

import javax.print.attribute.DocAttributeSet;

import javax.print.DocFlavor;

import net.jini.print.data.Doc;

/**
 * Class AbstractDoc is an abstract base class implementing interface {@link 
 * net.jini.print.data.Doc Doc} that provides one possible mechanism for a 
 * client to supply print data to a Print Job. Several concrete subclasses of 
 * class AbstractDoc allow a client to pass client formatted print data to a 
 * Print Job from a variety of sources. A Jini Print Service client can use one 
 * of the provided concrete subclasses of class AbstractDoc, can use its own 
 * extension of class AbstractDoc, or can use its own implementation of 
 * interface {@link net.jini.print.data.Doc Doc}. 
 * <P>
 * A subclass constructor must supply three things to class AbstractDoc's 
 * constructor: 
 * <UL>
 * <LI>
 * The print data format, an instance of class {@link 
 * javax.print.data.DocFlavor DocFlavor}, which consists of a MIME type and a 
 * representation class. 
 * <P>
 * <LI>
 * The print data representation object, if this object already exists. If this 
 * object doesn't already exist, the subclass constructor can supply null 
 * instead, but in that case the subclass must override the {@link 
 * #getPrintData() <CODE>getPrintData()</CODE>} method. 
 * <P>
 * <LI>
 * A set of printing attributes that specify additional characteristics of the 
 * doc and/or processing instructions for the doc. 
 * </UL>
 * Class AbstractDoc stores the above information in internal fields. If an 
 * AbstractDoc object is passed as an argument or return value of a remote 
 * method invocation (RMI), the doc flavor, print data representation object, 
 * and attributes are serialized and sent to the remote location inside the 
 * AbstractDoc object. Class AbstractDoc provides implementations for all of 
 * interface Doc's methods which merely return the corresponding objects 
 * supplied to the constructor. If a null print data representation object was 
 * supplied to the constructor, the subclass must override the {@link 
 * #getPrintData() <CODE>getPrintData()</CODE>} method to generate and return 
 * the print data representation object. 
 * <P>
 * <B>DRAFT STANDARD VERSION 1.0 (23-MAY-2000)</B>
 *
 * @author  Alan Kaminsky
 * @version 18-May-2000
 */
public abstract class AbstractDoc
	implements Doc, Serializable
	{

// Hidden data members.

	/**
	 * Supported doc flavor.
	 * @serial
	 */
	private DocFlavor myFlavor;

	/**
	 * Print data representation object.
	 * @serial
	 */
	private Object myData;

	/**
	 * Set of printing attributes, or null to use defaults.
	 * @serial
	 */
	private DocAttributeSet myAttributes;

// Exported constructors.

	/**
	 * Construct an abstract doc object.
	 *
	 * @param  theFlavor      Doc flavor supported.
	 * @param  theData        Print data representation object, or null if this
	 *                        object will be generated by the {@link 
	 *                        #getPrintData() <CODE>getPrintData()</CODE>} 
	 *                        method. If non-null, this object must be an 
	 *                        instance of the representation class whose name is 
	 *                        given by <CODE>theFlavor.{@link 
	 *                        javax.print.data.DocFlavor#getRepresentationClassName() 
	 *                        getRepresentationClassName()}</CODE>. 
	 * @param  theAttributes  Unmodifiable set of printing attributes, or null
	 *                        to request the Print Job's default values. 
	 *
	 * @exception  NullPointerException
	 *     (unchecked exception) Thrown if <CODE>theFlavor</CODE> is null.
	 */
	public AbstractDoc
		(DocFlavor theFlavor,
		 Object theData,
		 DocAttributeSet theAttributes)
		{
		if (theFlavor == null)
			{
			throw new NullPointerException
				("org.jpwg.jini.print.data.AbstractDoc.AbstractDoc(): theFlavor is null");
			}
		myFlavor = theFlavor;
		myData = theData;
		myAttributes = theAttributes;
		}

// Exported operations inherited and implemented from interface Doc.

	/**
	 * Determine the doc flavor in which this doc object will supply its piece 
	 * of print data. 
	 * <P>
	 * If this doc object is leased and the lease has expired or been canceled, 
	 * the behavior of this method is undefined. 
	 * <P>
	 * The doc flavor given to the constructor is returned.
	 *
	 * @return  Doc flavor.
	 *
	 * @exception  IllegalStateException
	 *     (unchecked exception) Thrown if this doc object is leased and its 
	 *     lease object has not yet been obtained (i.e., this doc object is an 
	 *     instance of the subinterface {@link net.jini.print.data.LeasedDoc 
	 *     LeasedDoc} and its {@link net.jini.print.data.LeasedDoc#getLease() 
	 *     <CODE>getLease()</CODE>} method has not yet been called). 
	 */
	public DocFlavor getDocFlavor()
		{
		return myFlavor;
		}

	/**
	 * Obtain the print data representation object that contains this doc 
	 * object's piece of print data in the format corresponding to the supported 
	 * doc flavor. The <CODE>getPrintData()</CODE> method returns an instance of 
	 * the representation class whose name is given by <CODE>{@link 
	 * #getDocFlavor() getDocFlavor()}.{@link 
	 * javax.print.data.DocFlavor#getRepresentationClassName() 
	 * getRepresentationClassName()}</CODE>, and the return value can be cast 
	 * from class Object to that representation class. 
	 * <P>
	 * If this doc object is leased and the lease has expired or been canceled, 
	 * the behavior of this method is undefined. 
	 * <P>
	 * The print data representation object given to the constructor (assumed to 
	 * be an instance of the proper class) is returned. If the print data 
	 * representation object is supposed to be generated when 
	 * <CODE>getPrintData()</CODE> is called, override this method in a 
	 * subclass. 
	 *
	 * @return  Print data representation object. 
	 *
	 * @exception  IOException
	 *     Thrown if the representation class is a stream and there was an I/O 
	 *     error while constructing the stream. 
	 * @exception  IllegalStateException
	 *     (unchecked exception) Thrown if this doc object is leased and its 
	 *     lease object has not yet been obtained (i.e., this doc object is an 
	 *     instance of the subinterface {@link net.jini.print.data.LeasedDoc 
	 *     LeasedDoc} and its {@link net.jini.print.data.LeasedDoc#getLease() 
	 *     <CODE>getLease()</CODE>} method has not yet been called). Also may be 
	 *     thrown (but is not required to be thrown) if the printer has not 
	 *     finished obtaining all the print data from a previous doc before 
	 *     requesting the print data from this doc. 
	 */
	public Object getPrintData()
		throws IOException
		{
		return myData;
		}

	/**
	 * Obtain the set of printing attributes for this doc object. If the 
	 * returned attribute set includes an instance of a particular attribute 
	 * <I>X,</I> the printer must use that attribute value for this doc, 
	 * overriding any value of attribute <I>X</I> in the Print Request's 
	 * attribute set. If the returned attribute set does not include an instance 
	 * of a particular attribute <I>X</I> or if null is returned, the printer 
	 * must consult the Print Request's attribute set to obtain the value for 
	 * attribute <I>X,</I> and if not found there, the printer must use an 
	 * implementation-dependent default value. The returned attribute set is 
	 * unmodifiable. 
	 * <P>
	 * If this doc object is leased and the lease has expired or been canceled, 
	 * the behavior of this method is undefined. 
	 * <P>
	 * The attribute set given to the constructor (assumed to be unmodifiable)
	 * is returned.
	 *
	 * @return  Unmodifiable set of printing attributes for this doc, or null to
	 *          obtain all attribute values from the Print Request's attribute 
	 *          set. 
	 * @exception  IllegalStateException
	 *     (unchecked exception) Thrown if this doc object is leased and its 
	 *     lease object has not yet been obtained (i.e., this doc object is an 
	 *     instance of the subinterface {@link net.jini.print.data.LeasedDoc 
	 *     LeasedDoc} and its {@link net.jini.print.data.LeasedDoc#getLease() 
	 *     <CODE>getLease()</CODE>} method has not yet been called). 
	 */
	public DocAttributeSet getAttributes()
		{
		return myAttributes;
		}

	/**
	 * Obtain a reader for extracting character print data from this doc. For 
	 * this method to work, this doc's flavor must have one of the following 
	 * print data representation classes: 
	 * <UL>
	 * <LI> char[]
	 * <LI> java.lang.String
	 * <LI> java.io.Reader
	 * <LI> byte[]
	 * <LI> java.io.InputStream
	 * </UL>
	 * For a byte array or input stream, this doc's flavor must also have a MIME 
	 * media type of <CODE>"text"</CODE> with an optional <CODE>"charset"</CODE> 
	 * parameter; the default character set is UTF-8. This doc's print data 
	 * representation object is obtained, then a reader for reading the print 
	 * data from the print data representation object as a stream of characters 
	 * is created and returned. However, if the print data representation object 
	 * is itself a reader, then the print data representation object is simply
	 * returned. 
	 * <P>
	 * If this doc fulfills the above criteria, then this method is required to 
	 * return a valid non-null reader object for reading the print data 
	 * characters from this doc. If this doc does not fulfill the above 
	 * criteria, then this method returns null to indicate a reader cannot be 
	 * provided. 
	 * <P>
	 * Note that if a reader needs to be layered on top of this doc's print data 
	 * representation object, the reader will be created and will execute in the 
	 * caller's Java Virtual Machine (JVM). If this doc's print data 
	 * representation object is a remote input stream, be aware that this method 
	 * will return a local reader layered on top of the remote input stream. 
	 * <P>
	 * If this doc object is leased and the lease has expired or been canceled, 
	 * the behavior of this method is undefined. 
	 * <P>
	 * This method's implementation in class AbstractDoc always returns null. If 
	 * a subclass fulfills the above criteria, override this method to return 
	 * the proper reader object. 
	 *
	 * @return  Reader for reading the print data characters from this doc. If a
	 *          reader cannot be provided because this doc does not meet the 
	 *          criteria stated above, null is returned. 
	 *
	 * @exception  UnsupportedEncodingException
	 *     Thrown if a plain text doc's character set is not supported.
	 * @exception  IOException
	 *     Thrown if there was an I/O error while creating the reader.
	 * @exception  IllegalStateException
	 *     (unchecked exception) Thrown if this doc object is leased and its 
	 *     lease object has not yet been obtained (i.e., this doc object is an 
	 *     instance of the subinterface {@link net.jini.print.data.LeasedDoc 
	 *     LeasedDoc} and its {@link net.jini.print.data.LeasedDoc#getLease() 
	 *     <CODE>getLease()</CODE>} method has not yet been called). Also may be 
	 *     thrown (but is not required to be thrown) if the printer has not 
	 *     finished obtaining all the print data from a previous doc before 
	 *     requesting the print data from this doc. 
	 */
	public Reader getReaderForText()
		throws UnsupportedEncodingException, IOException
		{
		return null;
		}

	/**
	 * Obtain an input stream for extracting byte print data from this doc. For 
	 * this method to work, this doc's flavor must have one of the following 
	 * print data representation classes: 
	 * <UL>
	 * <LI> byte[]
	 * <LI> java.io.InputStream
	 * </UL>
	 * This doc's print data representation object is obtained, then an input 
	 * stream for reading the print data from the print data representation 
	 * object as a stream of bytes is created and returned. However, if the 
	 * print data representation object is itself an input stream, then the 
	 * print data representation object is simply returned. 
	 * <P>
	 * If this doc fulfills the above criteria, then this method is required to 
	 * return a valid non-null input stream object for reading the print data 
	 * bytes from this doc. If this doc does not fulfill the above criteria, 
	 * then this method returns null to indicate an input stream cannot be 
	 * provided. 
	 * <P>
	 * Note that if an input stream needs to be layered on top of this doc's 
	 * print data representation object, the input stream will be created and 
	 * will execute in the caller's Java Virtual Machine (JVM). 
	 * <P>
	 * If this doc object is leased and the lease has expired or been canceled, 
	 * the behavior of this method is undefined. 
	 * <P>
	 * This method's implementation in class AbstractDoc always returns null. If 
	 * a subclass fulfills the above criteria, override this method to return 
	 * the proper input stream object. 
	 *
	 * @return  Input stream for reading the print data bytes from this doc. If
	 *          an input stream cannot be provided because this doc does not 
	 *          meet the criteria stated above, null is returned. 
	 *
	 * @exception  IOException
	 *     Thrown if there was an I/O error while creating the input stream.
	 * @exception  IllegalStateException
	 *     (unchecked exception) Thrown if this doc object is leased and its 
	 *     lease object has not yet been obtained (i.e., this doc object is an 
	 *     instance of the subinterface {@link net.jini.print.data.LeasedDoc 
	 *     LeasedDoc} and its {@link net.jini.print.data.LeasedDoc#getLease() 
	 *     <CODE>getLease()</CODE>} method has not yet been called). Also may be 
	 *     thrown (but is not required to be thrown) if the printer has not 
	 *     finished obtaining all the print data from a previous doc before 
	 *     requesting the print data from this doc. 
	 */
	public InputStream getStreamForBytes()
		throws IOException
		{
		return null;
		}

	}