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

package org.jpwg.jini.print.data;

import java.io.IOException;
import java.io.Reader;

import java.rmi.RemoteException;

import javax.print.attribute.DocAttributeSet;

import javax.print.DocFlavor;

/**
 * Class ReaderDoc is an implementation of interface {@link 
 * net.jini.print.data.Doc Doc} that provides the bytes read from a reader as a 
 * piece of print data in a doc flavor with MIME type given by the client and 
 * representation class name = <CODE>"java.io.Reader"</CODE>. The {@link 
 * #getPrintData() <CODE>getPrintData()</CODE>} method returns a Reader, and the 
 * return value can be cast from class Object to class Reader. 
 * <P>
 * To make the reader available to a potentially remote Jini Print Service 
 * instance, class ReaderDoc makes use of a remote reader (class {@link 
 * RmiReader RmiReader}) under the hood. 
 * <P>
 * To provide the contents of a local file with a given name as print data for a 
 * Print Job, use a ReaderDoc constructed as follows: 
 * <PRE>
 *     String theFileName = ...;
 *     FileInputStream theFileInputStream =
 *         new FileInputStream (theFileName);
 *     String theMimeType = ...;
 *     DocAttributeSet theAttributes = ...;
 *     ReaderDoc theDoc =
 *         new ReaderDoc
 *             (new InputStreamReader
 *                 (new BufferedInputStream (theFileInputStream),
 *                  "<I>encoding</I>"),
 *              theMimeType,
 *              theAttributes);
 * </PRE>
 * The BufferedInputStream is added on top of the FileInputStream to read the 
 * file in chunks, thus improving efficiency. The <I>encoding</I> specifies the 
 * character set the file uses; the InputStreamReader converts the file's bytes 
 * from the specified character set to Unicode characters. If a Jini Print 
 * Service instance doesn't support your file's character set but does support 
 * Readers (Unicode characters), you can still print the file by making the 
 * client do the character set conversion as shown above. (If a Jini Print 
 * Service instance does support your file's character set, just use an {@link 
 * InputStreamDoc InputStreamDoc} and avoid the conversion to Unicode.) 
 * <P>
 * Once the file is open, there is the possibility that the Print Job that is 
 * reading the file will not close the file due to a distributed failure. Thus, 
 * consider whether you need to provide a lease along with the doc (see 
 * interface {@link net.jini.print.data.LeasedDoc LeasedDoc} and class {@link 
 * LeasedReaderDoc LeasedReaderDoc}). For example, you could close the file when 
 * the lease expires or is canceled. 
 * <P>
 * To provide the contents of a local file denoted by a {@link java.io.File 
 * File} object as print data for a Print Job, use a ReaderDoc constructed as 
 * follows: 
 * <PRE>
 *     File theFile = ...;
 *     FileInputStream theFileInputStream =
 *         new FileInputStream (theFile);
 *     String theMimeType = ...;
 *     DocAttributeSet theAttributes = ...;
 *     ReaderDoc theDoc =
 *         new ReaderDoc
 *             (new InputStreamReader
 *                 (new BufferedInputStream (theFileInputStream),
 *                  "<I>encoding</I>"),
 *              theMimeType,
 *              theAttributes);
 * </PRE>
 * To provide the contents of a file at a URL address (denoted by a {@link 
 * java.net.URL URL} object) as print data for a Print Job, use a ReaderDoc 
 * constructed as follows: 
 * <PRE>
 *     URL theURL = ...;
 *     String theMimeType = ...;
 *     DocAttributeSet theAttributes = ...;
 *     ReaderDoc theDoc =
 *         new ReaderDoc
 *             (new InputStreamReader
 *                 (new BufferedInputStream (theURL.openStream()),
 *                  "<I>encoding</I>"),
 *              theMimeType,
 *              theAttributes);
 * </PRE>
 * Whenever you use a ReaderDoc object, you open a reader to read the file or 
 * other print data source locally on the client. The ReaderDoc object then 
 * passes that reader to the Print Job as the print data representation object. 
 * Thus, in the case of a URL, the printer will go through the client to get the 
 * print data; the printer will not access the URL directly. (For a {@link 
 * net.jini.print.data.Doc Doc} object where the printer accesses a URL 
 * directly, see class {@link URLDoc URLDoc}.) 
 * <P>
 * <B>DRAFT STANDARD VERSION 1.0 (23-MAY-2000)</B>
 *
 * @author  Alan Kaminsky
 * @version 18-May-2000
 */
public class ReaderDoc
	extends AbstractDoc
	{

// Hidden data members.

	/**
	 * Original reader. This field is <I>not</I> serialized. In a remote JVM, 
	 * this field is null. A remote reader wrapping the original input stream is 
	 * stored in the superclass as the print data representation object. 
	 */
	private transient Reader myOriginalReader;

// Exported constructors.

	/**
	 * Construct a reader doc object. 
	 *
	 * @param  theData        Character stream to be printed.
	 * @param  theMimeType    MIME media type string giving the format of
	 *                        <CODE>theData</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>theData</CODE> is null or
	 *     <CODE>theMimeType</CODE> is null.
	 * @exception  IllegalArgumentException
	 *     (unchecked exception) Thrown if <CODE>theMimeType</CODE> does not 
	 *     obey the syntax for a MIME media type string.
	 * @exception  RemoteException
	 *     Thrown if a remote error occurred while constructing the hidden 
	 *     remote reader. 
	 */
	public ReaderDoc
		(Reader theData,
		 String theMimeType,
		 DocAttributeSet theAttributes)
		throws RemoteException
		{
		super
			(new DocFlavor.READER (theMimeType),
			 new RmiReader (verify (theData)),
			 theAttributes);
		myOriginalReader = theData;
		}

	private static Reader verify
		(Reader theData)
		{
		if (theData == null)
			{
			throw new NullPointerException
				("org.jpwg.jini.print.data.ReaderDoc.ReaderDoc(): theData is null");
			}
		return theData;
		}

// Exported operations inherited and overridden from class AbstractDoc.

	/**
	 * 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 a 
	 * java.io.Reader, and the return value can be cast from class Object to 
	 * class java.io.Reader. 
	 * <P>
	 * If this doc object is sent to a remote location, 
	 * <CODE>getPrintData()</CODE> will return a remote reader that allows the 
	 * remote Print Job to read the original reader over RMI. On the local JVM 
	 * that created this doc object, <CODE>getPrintData()</CODE> will return the 
	 * original reader, allowing the Print Job to read the original reader 
	 * directly. 
	 *
	 * @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. 
	 */
	public Object getPrintData()
		throws IOException
		{
		return
			myOriginalReader == null ?
				super.getPrintData() :
				myOriginalReader;
		}

	/**
	 * Obtain a reader for extracting character print data from this doc. This 
	 * method merely returns the same print data representation object (reader) 
	 * that {@link #getPrintData() <CODE>getPrintData()</CODE>} returns. 
	 *
	 * @return  Reader for reading the print data characters from this doc.
	 *
	 * @exception  IOException
	 *     Thrown if there was an I/O error while creating the reader.
	 */
	public Reader getReaderForText()
		throws IOException
		{
		return (Reader) getPrintData();
		}

	}