1 /*
   2  * Copyright (c) 2001, 2017, 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.IOException;
  31 import java.io.InputStream;
  32 import java.io.Reader;
  33 import java.io.StringReader;
  34 
  35 import javax.print.attribute.AttributeSetUtilities;
  36 import javax.print.attribute.DocAttributeSet;
  37 
  38 /**
  39  * This class is an implementation of interface {@code Doc} that can be used in
  40  * many common printing requests. It can handle all of the presently defined
  41  * "pre-defined" doc flavors defined as static variables in the
  42  * {@code DocFlavor} class.
  43  * <p>
  44  * In particular this class implements certain required semantics of the
  45  * {@code Doc} specification as follows:
  46  * <ul>
  47  *   <li>constructs a stream for the service if requested and appropriate.
  48  *   <li>ensures the same object is returned for each call on a method.
  49  *   <li>ensures multiple threads can access the {@code Doc}
  50  *   <li>performs some validation of that the data matches the doc flavor.
  51  * </ul>
  52  * Clients who want to re-use the doc object in other jobs, or need a
  53  * {@code MultiDoc} will not want to use this class.
  54  * <p>
  55  * If the print data is a stream, or a print job requests data as a stream, then
  56  * {@code SimpleDoc} does not monitor if the service properly closes the stream
  57  * after data transfer completion or job termination. Clients may prefer to use
  58  * provide their own implementation of doc that adds a listener to monitor job
  59  * completion and to validate that resources such as streams are freed (ie
  60  * closed).
  61  */
  62 public final class SimpleDoc implements Doc {
  63 
  64     /**
  65      * The doc flavor in which this doc will supply its piece of print data.
  66      */
  67     private DocFlavor flavor;
  68 
  69     /**
  70      * The set of printing attributes for this doc.
  71      */
  72     private DocAttributeSet attributes;
  73 
  74     /**
  75      * The print data.
  76      */
  77     private Object printData;
  78 
  79     /**
  80      * The reader for extracting character print data from this doc.
  81      */
  82     private Reader reader;
  83 
  84     /**
  85      * The input stream for extracting byte print data from this doc.
  86      */
  87     private InputStream inStream;
  88 
  89     /**
  90      * Constructs a {@code SimpleDoc} with the specified print data, doc flavor
  91      * and doc attribute set.
  92      *
  93      * @param  printData the print data object
  94      * @param  flavor the {@code DocFlavor} object
  95      * @param  attributes a {@code DocAttributeSet}, which can be {@code null}
  96      * @throws IllegalArgumentException if {@code flavor} or {@code printData}
  97      *         is {@code null}, or the {@code printData} does not correspond to
  98      *         the specified doc flavor--for example, the data is not of the
  99      *         type specified as the representation in the {@code DocFlavor}
 100      */
 101     public SimpleDoc(Object printData,
 102                      DocFlavor flavor, DocAttributeSet attributes) {
 103 
 104        if (flavor == null || printData == null) {
 105            throw new IllegalArgumentException("null argument(s)");
 106        }
 107 
 108        Class<?> repClass = null;
 109        try {
 110             String className = flavor.getRepresentationClassName();
 111             sun.reflect.misc.ReflectUtil.checkPackageAccess(className);
 112             repClass = Class.forName(className, false,
 113                               Thread.currentThread().getContextClassLoader());
 114        } catch (Throwable e) {
 115            throw new IllegalArgumentException("unknown representation class");
 116        }
 117 
 118        if (!repClass.isInstance(printData)) {
 119            throw new IllegalArgumentException("data is not of declared type");
 120        }
 121 
 122        this.flavor = flavor;
 123        if (attributes != null) {
 124            this.attributes = AttributeSetUtilities.unmodifiableView(attributes);
 125        }
 126        this.printData = printData;
 127     }
 128 
 129     /**
 130      * Determines the doc flavor in which this doc object will supply its piece
 131      * of print data.
 132      *
 133      * @return doc flavor
 134      */
 135     public DocFlavor getDocFlavor() {
 136         return flavor;
 137     }
 138 
 139     /**
 140      * Obtains the set of printing attributes for this doc object. If the
 141      * returned attribute set includes an instance of a particular attribute
 142      * <i>X,</i> the printer must use that attribute value for this doc,
 143      * overriding any value of attribute <i>X</i> in the job's attribute set. If
 144      * the returned attribute set does not include an instance of a particular
 145      * attribute <i>X</i> or if {@code null} is returned, the printer must
 146      * consult the job's attribute set to obtain the value for attribute
 147      * <i>X,</i> and if not found there, the printer must use an
 148      * implementation-dependent default value. The returned attribute set is
 149      * unmodifiable.
 150      *
 151      * @return unmodifiable set of printing attributes for this doc, or
 152      *         {@code null} to obtain all attribute values from the job's
 153      *         attribute set
 154      */
 155     public DocAttributeSet getAttributes() {
 156         return attributes;
 157     }
 158 
 159     /**
 160      * Obtains the print data representation object that contains this doc
 161      * object's piece of print data in the format corresponding to the supported
 162      * doc flavor. The {@code getPrintData()} method returns an instance of the
 163      * representation class whose name is given by {@link #getDocFlavor()
 164      * getDocFlavor()}.{@link DocFlavor#getRepresentationClassName()
 165      * getRepresentationClassName()}, and the return value can be cast from
 166      * class {@code Object} to that representation class.
 167      *
 168      * @return print data representation object
 169      * @throws IOException if the representation class is a stream and there was
 170      *         an I/O error while constructing the stream
 171      */
 172     public Object getPrintData() throws IOException {
 173         return printData;
 174     }
 175 
 176     /**
 177      * Obtains a reader for extracting character print data from this doc. The
 178      * {@code Doc} implementation is required to support this method if the
 179      * {@code DocFlavor} has one of the following print data representation
 180      * classes, and return {@code null} otherwise:
 181      * <ul>
 182      *   <li>{@code char[]}
 183      *   <li>{@code java.lang.String}
 184      *   <li>{@code java.io.Reader}
 185      * </ul>
 186      * The doc's print data representation object is used to construct and
 187      * return a {@code Reader} for reading the print data as a stream of
 188      * characters from the print data representation object. However, if the
 189      * print data representation object is itself a {@code Reader} then the
 190      * print data representation object is simply returned.
 191      *
 192      * @return a {@code Reader} for reading the print data characters from this
 193      *         doc. If a reader cannot be provided because this doc does not
 194      *         meet the criteria stated above, {@code null} is returned.
 195      * @throws IOException if there was an I/O error while creating the reader
 196      */
 197     public Reader getReaderForText() throws IOException {
 198 
 199         if (printData instanceof Reader) {
 200             return (Reader)printData;
 201         }
 202 
 203         synchronized (this) {
 204             if (reader != null) {
 205                 return reader;
 206             }
 207 
 208             if (printData instanceof char[]) {
 209                reader = new CharArrayReader((char[])printData);
 210             }
 211             else if (printData instanceof String) {
 212                 reader = new StringReader((String)printData);
 213             }
 214         }
 215         return reader;
 216     }
 217 
 218     /**
 219      * Obtains an input stream for extracting byte print data from this doc. The
 220      * {@code Doc} implementation is required to support this method if the
 221      * {@code DocFlavor} has one of the following print data representation
 222      * classes; otherwise this method returns {@code null}:
 223      * <ul>
 224      *   <li>{@code byte[]}
 225      *   <li>{@code java.io.InputStream}
 226      * </ul>
 227      * The doc's print data representation object is obtained. Then, an input
 228      * stream for reading the print data from the print data representation
 229      * object as a stream of bytes is created and returned. However, if the
 230      * print data representation object is itself an input stream then the print
 231      * data representation object is simply returned.
 232      *
 233      * @return an {@code InputStream} for reading the print data bytes from this
 234      *         doc. If an input stream cannot be provided because this doc does
 235      *         not meet the criteria stated above, {@code null} is returned.
 236      * @throws IOException if there was an I/O error while creating the input
 237      *         stream
 238      */
 239     public InputStream getStreamForBytes() throws IOException {
 240 
 241         if (printData instanceof InputStream) {
 242             return (InputStream)printData;
 243         }
 244 
 245         synchronized (this) {
 246             if (inStream != null) {
 247                 return inStream;
 248             }
 249 
 250             if (printData instanceof byte[]) {
 251                inStream = new ByteArrayInputStream((byte[])printData);
 252             }
 253         }
 254         return inStream;
 255     }
 256 }