1 /*
   2  * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.print;
  27 
  28 import java.io.ByteArrayInputStream;
  29 import java.io.CharArrayReader;
  30 import java.io.StringReader;
  31 import java.io.InputStream;
  32 import java.io.IOException;
  33 import java.io.Reader;
  34 import javax.print.attribute.AttributeSetUtilities;
  35 import javax.print.attribute.DocAttributeSet;
  36 
  37 /**
  38  * This class is an implementation of interface <code>Doc</code> that can
  39  * be used in many common printing requests.
  40  * It can handle all of the presently defined "pre-defined" doc flavors
  41  * defined as static variables in the DocFlavor class.
  42  * <p>
  43  * In particular this class implements certain required semantics of the
  44  * Doc specification as follows:
  45  * <ul>
  46  * <li>constructs a stream for the service if requested and appropriate.
  47  * <li>ensures the same object is returned for each call on a method.
  48  * <li>ensures multiple threads can access the Doc
  49  * <li>performs some validation of that the data matches the doc flavor.
  50  * </ul>
  51  * Clients who want to re-use the doc object in other jobs,
  52  * or need a MultiDoc will not want to use this class.
  53  * <p>
  54  * If the print data is a stream, or a print job requests data as a
  55  * stream, then <code>SimpleDoc</code> does not monitor if the service
  56  * properly closes the stream after data transfer completion or job
  57  * termination.
  58  * Clients may prefer to use provide their own implementation of doc that
  59  * adds a listener to monitor job completion and to validate that
  60  * resources such as streams are freed (ie closed).
  61  */
  62 
  63 public final class SimpleDoc implements Doc {
  64 
  65     private DocFlavor flavor;
  66     private DocAttributeSet attributes;
  67     private Object printData;
  68     private Reader reader;
  69     private InputStream inStream;
  70 
  71     /**
  72      * Constructs a <code>SimpleDoc</code> with the specified
  73      * print data, doc flavor and doc attribute set.
  74      * @param printData the print data object
  75      * @param flavor the <code>DocFlavor</code> object
  76      * @param attributes a <code>DocAttributeSet</code>, which can
  77      *                   be <code>null</code>
  78      * @throws IllegalArgumentException if <code>flavor</code> or
  79      *         <code>printData</code> is <code>null</code>, or the
  80      *         <code>printData</code> does not correspond
  81      *         to the specified doc flavor--for example, the data is
  82      *         not of the type specified as the representation in the
  83      *         <code>DocFlavor</code>.
  84      */
  85     public SimpleDoc(Object printData,
  86                      DocFlavor flavor, DocAttributeSet attributes) {
  87 
  88        if (flavor == null || printData == null) {
  89            throw new IllegalArgumentException("null argument(s)");
  90        }
  91 
  92        Class<?> repClass = null;
  93        try {
  94             String className = flavor.getRepresentationClassName();
  95             sun.reflect.misc.ReflectUtil.checkPackageAccess(className);
  96             repClass = Class.forName(className, false,
  97                               Thread.currentThread().getContextClassLoader());
  98        } catch (Throwable e) {
  99            throw new IllegalArgumentException("unknown representation class");
 100        }
 101 
 102        if (!repClass.isInstance(printData)) {
 103            throw new IllegalArgumentException("data is not of declared type");
 104        }
 105 
 106        this.flavor = flavor;
 107        if (attributes != null) {
 108            this.attributes = AttributeSetUtilities.unmodifiableView(attributes);
 109        }
 110        this.printData = printData;
 111     }
 112 
 113    /**
 114      * Determines the doc flavor in which this doc object will supply its
 115      * piece of print data.
 116      *
 117      * @return  Doc flavor.
 118      */
 119     public DocFlavor getDocFlavor() {
 120         return flavor;
 121     }
 122 
 123     /**
 124      * Obtains the set of printing attributes for this doc object. If the
 125      * returned attribute set includes an instance of a particular attribute
 126      * <I>X,</I> the printer must use that attribute value for this doc,
 127      * overriding any value of attribute <I>X</I> in the job's attribute set.
 128      * If the returned attribute set does not include an instance
 129      * of a particular attribute <I>X</I> or if null is returned, the printer
 130      * must consult the job's attribute set to obtain the value for
 131      * attribute <I>X,</I> and if not found there, the printer must use an
 132      * implementation-dependent default value. The returned attribute set is
 133      * unmodifiable.
 134      *
 135      * @return  Unmodifiable set of printing attributes for this doc, or null
 136      *          to obtain all attribute values from the job's attribute
 137      *          set.
 138      */
 139     public DocAttributeSet getAttributes() {
 140         return attributes;
 141     }
 142 
 143     /*
 144      * Obtains the print data representation object that contains this doc
 145      * object's piece of print data in the format corresponding to the
 146      * supported doc flavor.
 147      * The <CODE>getPrintData()</CODE> method returns an instance of
 148      * the representation class whose name is given by
 149      * {@link DocFlavor#getRepresentationClassName() getRepresentationClassName},
 150      * and the return value can be cast
 151      * from class Object to that representation class.
 152      *
 153      * @return  Print data representation object.
 154      *
 155      * @exception  IOException if the representation class is a stream and
 156      *     there was an I/O error while constructing the stream.
 157      */
 158     public Object getPrintData() throws IOException {
 159         return printData;
 160     }
 161 
 162     /**
 163      * Obtains a reader for extracting character print data from this doc.
 164      * The <code>Doc</code> implementation is required to support this
 165      * method if the <code>DocFlavor</code> has one of the following print
 166      * data representation classes, and return <code>null</code>
 167      * otherwise:
 168      * <UL>
 169      * <LI> <code>char[]</code>
 170      * <LI> <code>java.lang.String</code>
 171      * <LI> <code>java.io.Reader</code>
 172      * </UL>
 173      * The doc's print data representation object is used to construct and
 174      * return a <code>Reader</code> for reading the print data as a stream
 175      * of characters from the print data representation object.
 176      * However, if the print data representation object is itself a
 177      * <code>Reader</code> then the print data representation object is
 178      * simply returned.
 179      * <P>
 180      * @return  a <code>Reader</code> for reading the print data
 181      *          characters from this doc.
 182      *          If a reader cannot be provided because this doc does not meet
 183      *          the criteria stated above, <code>null</code> is returned.
 184      *
 185      * @exception  IOException if there was an I/O error while creating
 186      *             the reader.
 187      */
 188     public Reader getReaderForText() throws IOException {
 189 
 190         if (printData instanceof Reader) {
 191             return (Reader)printData;
 192         }
 193 
 194         synchronized (this) {
 195             if (reader != null) {
 196                 return reader;
 197             }
 198 
 199             if (printData instanceof char[]) {
 200                reader = new CharArrayReader((char[])printData);
 201             }
 202             else if (printData instanceof String) {
 203                 reader = new StringReader((String)printData);
 204             }
 205         }
 206         return reader;
 207     }
 208 
 209     /**
 210      * Obtains an input stream for extracting byte print data from
 211      * this doc.
 212      * The <code>Doc</code> implementation is required to support this
 213      * method if the <code>DocFlavor</code> has one of the following print
 214      * data representation classes; otherwise this method
 215      * returns <code>null</code>:
 216      * <UL>
 217      * <LI> <code>byte[]</code>
 218      * <LI> <code>java.io.InputStream</code>
 219      * </UL>
 220      * The doc's print data representation object is obtained.  Then, an
 221      * input stream for reading the print data
 222      * from the print data representation object as a stream of bytes is
 223      * created and returned.
 224      * However, if the print data representation object is itself an
 225      * input stream then the print data representation object is simply
 226      * returned.
 227      * <P>
 228      * @return  an <code>InputStream</code> for reading the print data
 229      *          bytes from this doc.  If an input stream cannot be
 230      *          provided because this doc does not meet
 231      *          the criteria stated above, <code>null</code> is returned.
 232      *
 233      * @exception  IOException
 234      *     if there was an I/O error while creating the input stream.
 235      */
 236     public InputStream getStreamForBytes() throws IOException {
 237 
 238         if (printData instanceof InputStream) {
 239             return (InputStream)printData;
 240         }
 241 
 242         synchronized (this) {
 243             if (inStream != null) {
 244                 return inStream;
 245             }
 246 
 247             if (printData instanceof byte[]) {
 248                inStream = new ByteArrayInputStream((byte[])printData);
 249             }
 250         }
 251         return inStream;
 252     }
 253 
 254 }