src/java.desktop/unix/classes/sun/print/IPPPrintService.java
changeset 47216 71c04702a3d5
parent 43723 f7a55f9b9bf5
child 50486 143c539c00dc
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2003, 2014, 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 sun.print;
       
    27 
       
    28 import javax.print.attribute.*;
       
    29 import javax.print.attribute.standard.*;
       
    30 import javax.print.DocFlavor;
       
    31 import javax.print.DocPrintJob;
       
    32 import javax.print.PrintService;
       
    33 import javax.print.ServiceUIFactory;
       
    34 import java.util.ArrayList;
       
    35 import java.util.HashMap;
       
    36 import java.util.Locale;
       
    37 import java.util.Date;
       
    38 import java.util.Arrays;
       
    39 import java.security.AccessController;
       
    40 import java.security.PrivilegedActionException;
       
    41 import java.security.PrivilegedExceptionAction;
       
    42 import javax.print.event.PrintServiceAttributeListener;
       
    43 
       
    44 import java.net.URI;
       
    45 import java.net.URISyntaxException;
       
    46 import java.net.URL;
       
    47 import java.net.URLConnection;
       
    48 import java.net.HttpURLConnection;
       
    49 import java.io.File;
       
    50 import java.io.InputStream;
       
    51 import java.io.OutputStream;
       
    52 import java.io.OutputStreamWriter;
       
    53 import java.io.DataInputStream;
       
    54 import java.io.ByteArrayOutputStream;
       
    55 import java.io.ByteArrayInputStream;
       
    56 import java.io.BufferedReader;
       
    57 import java.io.InputStreamReader;
       
    58 import java.nio.charset.Charset;
       
    59 
       
    60 import java.util.Iterator;
       
    61 import java.util.HashSet;
       
    62 import java.util.Map;
       
    63 
       
    64 
       
    65 public class IPPPrintService implements PrintService, SunPrinterJobService {
       
    66 
       
    67     public static final boolean debugPrint;
       
    68     private static final String debugPrefix = "IPPPrintService>> ";
       
    69     protected static void debug_println(String str) {
       
    70         if (debugPrint) {
       
    71             System.out.println(str);
       
    72         }
       
    73     }
       
    74 
       
    75     private static final String FORCE_PIPE_PROP = "sun.print.ippdebug";
       
    76 
       
    77     static {
       
    78         String debugStr = java.security.AccessController.doPrivileged(
       
    79                   new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP));
       
    80 
       
    81         debugPrint = "true".equalsIgnoreCase(debugStr);
       
    82     }
       
    83 
       
    84     private String printer;
       
    85     private URI    myURI;
       
    86     private URL    myURL;
       
    87     private transient ServiceNotifier notifier = null;
       
    88 
       
    89     private static int MAXCOPIES = 1000;
       
    90     private static short MAX_ATTRIBUTE_LENGTH = 255;
       
    91 
       
    92     private CUPSPrinter cps;
       
    93     private HttpURLConnection urlConnection = null;
       
    94     private DocFlavor[] supportedDocFlavors;
       
    95     private Class<?>[] supportedCats;
       
    96     private MediaTray[] mediaTrays;
       
    97     private MediaSizeName[] mediaSizeNames;
       
    98     private CustomMediaSizeName[] customMediaSizeNames;
       
    99     private int defaultMediaIndex;
       
   100     private int[] rawResolutions = null;
       
   101     private PrinterResolution[] printerResolutions = null;
       
   102     private boolean isCupsPrinter;
       
   103     private boolean init;
       
   104     private Boolean isPS;
       
   105     private HashMap<String, AttributeClass> getAttMap;
       
   106     private boolean pngImagesAdded = false;
       
   107     private boolean gifImagesAdded = false;
       
   108     private boolean jpgImagesAdded = false;
       
   109 
       
   110 
       
   111     /**
       
   112      * IPP Status Codes
       
   113      */
       
   114     private static final byte STATUSCODE_SUCCESS = 0x00;
       
   115 
       
   116     /**
       
   117      * IPP Group Tags.  Each tag is used once before the first attribute
       
   118      * of that group.
       
   119      */
       
   120     // operation attributes group
       
   121     private static final byte GRPTAG_OP_ATTRIBUTES = 0x01;
       
   122     // job attributes group
       
   123     private static final byte GRPTAG_JOB_ATTRIBUTES = 0x02;
       
   124     // printer attributes group
       
   125     private static final byte GRPTAG_PRINTER_ATTRIBUTES = 0x04;
       
   126     // used as the last tag in an IPP message.
       
   127     private static final byte GRPTAG_END_ATTRIBUTES = 0x03;
       
   128 
       
   129     /**
       
   130      * IPP Operation codes
       
   131      */
       
   132     // gets the attributes for a printer
       
   133     public static final String OP_GET_ATTRIBUTES = "000B";
       
   134     // gets the default printer
       
   135     public static final String OP_CUPS_GET_DEFAULT = "4001";
       
   136     // gets the list of printers
       
   137     public static final String OP_CUPS_GET_PRINTERS = "4002";
       
   138 
       
   139 
       
   140     /**
       
   141      * List of all PrintRequestAttributes.  This is used
       
   142      * for looping through all the IPP attribute name.
       
   143      */
       
   144     private static Object[] printReqAttribDefault = {
       
   145         Chromaticity.COLOR,
       
   146         new Copies(1),
       
   147         Fidelity.FIDELITY_FALSE,
       
   148         Finishings.NONE,
       
   149         //new JobHoldUntil(new Date()),
       
   150         //new JobImpressions(0),
       
   151         //JobImpressions,
       
   152         //JobKOctets,
       
   153         //JobMediaSheets,
       
   154         new JobName("", Locale.getDefault()),
       
   155         //JobPriority,
       
   156         JobSheets.NONE,
       
   157         (Media)MediaSizeName.NA_LETTER,
       
   158         //MediaPrintableArea.class, // not an IPP attribute
       
   159         //MultipleDocumentHandling.SINGLE_DOCUMENT,
       
   160         new NumberUp(1),
       
   161         OrientationRequested.PORTRAIT,
       
   162         new PageRanges(1),
       
   163         //PresentationDirection,
       
   164                  // CUPS does not supply printer-resolution attribute
       
   165         //new PrinterResolution(300, 300, PrinterResolution.DPI),
       
   166         //PrintQuality.NORMAL,
       
   167         new RequestingUserName("", Locale.getDefault()),
       
   168         //SheetCollate.UNCOLLATED, //CUPS has no sheet collate?
       
   169         Sides.ONE_SIDED,
       
   170     };
       
   171 
       
   172 
       
   173     /**
       
   174      * List of all PrintServiceAttributes.  This is used
       
   175      * for looping through all the IPP attribute name.
       
   176      */
       
   177     private static Object[][] serviceAttributes = {
       
   178         {ColorSupported.class, "color-supported"},
       
   179         {PagesPerMinute.class,  "pages-per-minute"},
       
   180         {PagesPerMinuteColor.class, "pages-per-minute-color"},
       
   181         {PDLOverrideSupported.class, "pdl-override-supported"},
       
   182         {PrinterInfo.class, "printer-info"},
       
   183         {PrinterIsAcceptingJobs.class, "printer-is-accepting-jobs"},
       
   184         {PrinterLocation.class, "printer-location"},
       
   185         {PrinterMakeAndModel.class, "printer-make-and-model"},
       
   186         {PrinterMessageFromOperator.class, "printer-message-from-operator"},
       
   187         {PrinterMoreInfo.class, "printer-more-info"},
       
   188         {PrinterMoreInfoManufacturer.class, "printer-more-info-manufacturer"},
       
   189         {PrinterName.class, "printer-name"},
       
   190         {PrinterState.class, "printer-state"},
       
   191         {PrinterStateReasons.class, "printer-state-reasons"},
       
   192         {PrinterURI.class, "printer-uri"},
       
   193         {QueuedJobCount.class, "queued-job-count"}
       
   194     };
       
   195 
       
   196 
       
   197     /**
       
   198      * List of DocFlavors, grouped based on matching mime-type.
       
   199      * NOTE: For any change in the predefined DocFlavors, it must be reflected
       
   200      * here also.
       
   201      */
       
   202     // PDF DocFlavors
       
   203     private static DocFlavor[] appPDF = {
       
   204         DocFlavor.BYTE_ARRAY.PDF,
       
   205         DocFlavor.INPUT_STREAM.PDF,
       
   206         DocFlavor.URL.PDF
       
   207     };
       
   208 
       
   209     // Postscript DocFlavors
       
   210     private static DocFlavor[] appPostScript = {
       
   211         DocFlavor.BYTE_ARRAY.POSTSCRIPT,
       
   212         DocFlavor.INPUT_STREAM.POSTSCRIPT,
       
   213         DocFlavor.URL.POSTSCRIPT
       
   214     };
       
   215 
       
   216     // Autosense DocFlavors
       
   217     private static DocFlavor[] appOctetStream = {
       
   218         DocFlavor.BYTE_ARRAY.AUTOSENSE,
       
   219         DocFlavor.INPUT_STREAM.AUTOSENSE,
       
   220         DocFlavor.URL.AUTOSENSE
       
   221     };
       
   222 
       
   223     // Text DocFlavors
       
   224     private static DocFlavor[] textPlain = {
       
   225         DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_8,
       
   226         DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16,
       
   227         DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16BE,
       
   228         DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16LE,
       
   229         DocFlavor.BYTE_ARRAY.TEXT_PLAIN_US_ASCII,
       
   230         DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_8,
       
   231         DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16,
       
   232         DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16BE,
       
   233         DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16LE,
       
   234         DocFlavor.INPUT_STREAM.TEXT_PLAIN_US_ASCII,
       
   235         DocFlavor.URL.TEXT_PLAIN_UTF_8,
       
   236         DocFlavor.URL.TEXT_PLAIN_UTF_16,
       
   237         DocFlavor.URL.TEXT_PLAIN_UTF_16BE,
       
   238         DocFlavor.URL.TEXT_PLAIN_UTF_16LE,
       
   239         DocFlavor.URL.TEXT_PLAIN_US_ASCII,
       
   240         DocFlavor.CHAR_ARRAY.TEXT_PLAIN,
       
   241         DocFlavor.STRING.TEXT_PLAIN,
       
   242         DocFlavor.READER.TEXT_PLAIN
       
   243     };
       
   244 
       
   245     private static DocFlavor[] textPlainHost = {
       
   246         DocFlavor.BYTE_ARRAY.TEXT_PLAIN_HOST,
       
   247         DocFlavor.INPUT_STREAM.TEXT_PLAIN_HOST,
       
   248         DocFlavor.URL.TEXT_PLAIN_HOST
       
   249     };
       
   250 
       
   251     // JPG DocFlavors
       
   252     private static DocFlavor[] imageJPG = {
       
   253         DocFlavor.BYTE_ARRAY.JPEG,
       
   254         DocFlavor.INPUT_STREAM.JPEG,
       
   255         DocFlavor.URL.JPEG
       
   256     };
       
   257 
       
   258     // GIF DocFlavors
       
   259     private static DocFlavor[] imageGIF = {
       
   260         DocFlavor.BYTE_ARRAY.GIF,
       
   261         DocFlavor.INPUT_STREAM.GIF,
       
   262         DocFlavor.URL.GIF
       
   263     };
       
   264 
       
   265     // PNG DocFlavors
       
   266     private static DocFlavor[] imagePNG = {
       
   267         DocFlavor.BYTE_ARRAY.PNG,
       
   268         DocFlavor.INPUT_STREAM.PNG,
       
   269         DocFlavor.URL.PNG
       
   270     };
       
   271 
       
   272     // HTML DocFlavors
       
   273     private  static DocFlavor[] textHtml = {
       
   274         DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_8,
       
   275         DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16,
       
   276         DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16BE,
       
   277         DocFlavor.BYTE_ARRAY.TEXT_HTML_UTF_16LE,
       
   278         DocFlavor.BYTE_ARRAY.TEXT_HTML_US_ASCII,
       
   279         DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_8,
       
   280         DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16,
       
   281         DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16BE,
       
   282         DocFlavor.INPUT_STREAM.TEXT_HTML_UTF_16LE,
       
   283         DocFlavor.INPUT_STREAM.TEXT_HTML_US_ASCII,
       
   284         DocFlavor.URL.TEXT_HTML_UTF_8,
       
   285         DocFlavor.URL.TEXT_HTML_UTF_16,
       
   286         DocFlavor.URL.TEXT_HTML_UTF_16BE,
       
   287         DocFlavor.URL.TEXT_HTML_UTF_16LE,
       
   288         DocFlavor.URL.TEXT_HTML_US_ASCII,
       
   289         // These are not handled in UnixPrintJob so commenting these
       
   290         // for now.
       
   291         /*
       
   292         DocFlavor.CHAR_ARRAY.TEXT_HTML,
       
   293         DocFlavor.STRING.TEXT_HTML,
       
   294         DocFlavor.READER.TEXT_HTML,
       
   295         */
       
   296     };
       
   297 
       
   298     private  static DocFlavor[] textHtmlHost = {
       
   299         DocFlavor.BYTE_ARRAY.TEXT_HTML_HOST,
       
   300         DocFlavor.INPUT_STREAM.TEXT_HTML_HOST,
       
   301         DocFlavor.URL.TEXT_HTML_HOST,
       
   302     };
       
   303 
       
   304 
       
   305     // PCL DocFlavors
       
   306     private static DocFlavor[] appPCL = {
       
   307         DocFlavor.BYTE_ARRAY.PCL,
       
   308         DocFlavor.INPUT_STREAM.PCL,
       
   309         DocFlavor.URL.PCL
       
   310     };
       
   311 
       
   312     // List of all DocFlavors, used in looping
       
   313     // through all supported mime-types
       
   314     private static Object[] allDocFlavors = {
       
   315         appPDF, appPostScript, appOctetStream,
       
   316         textPlain, imageJPG, imageGIF, imagePNG,
       
   317         textHtml, appPCL,
       
   318     };
       
   319 
       
   320 
       
   321     IPPPrintService(String name, URL url) {
       
   322         if ((name == null) || (url == null)){
       
   323             throw new IllegalArgumentException("null uri or printer name");
       
   324         }
       
   325         try {
       
   326             printer = java.net.URLDecoder.decode(name, "UTF-8");
       
   327         } catch (java.io.UnsupportedEncodingException e) {
       
   328             printer = name;
       
   329         }
       
   330         supportedDocFlavors = null;
       
   331         supportedCats = null;
       
   332         mediaSizeNames = null;
       
   333         customMediaSizeNames = null;
       
   334         mediaTrays = null;
       
   335         myURL = url;
       
   336         cps = null;
       
   337         isCupsPrinter = false;
       
   338         init = false;
       
   339         defaultMediaIndex = -1;
       
   340 
       
   341         String host = myURL.getHost();
       
   342         if (host!=null && host.equals(CUPSPrinter.getServer())) {
       
   343             isCupsPrinter = true;
       
   344             try {
       
   345                 myURI =  new URI("ipp://"+host+
       
   346                                  "/printers/"+printer);
       
   347                 debug_println(debugPrefix+"IPPPrintService myURI : "+myURI);
       
   348             } catch (java.net.URISyntaxException e) {
       
   349                 throw new IllegalArgumentException("invalid url");
       
   350             }
       
   351         }
       
   352     }
       
   353 
       
   354 
       
   355     IPPPrintService(String name, String uriStr, boolean isCups) {
       
   356         if ((name == null) || (uriStr == null)){
       
   357             throw new IllegalArgumentException("null uri or printer name");
       
   358         }
       
   359         try {
       
   360             printer = java.net.URLDecoder.decode(name, "UTF-8");
       
   361         } catch (java.io.UnsupportedEncodingException e) {
       
   362             printer = name;
       
   363         }
       
   364         supportedDocFlavors = null;
       
   365         supportedCats = null;
       
   366         mediaSizeNames = null;
       
   367         customMediaSizeNames = null;
       
   368         mediaTrays = null;
       
   369         cps = null;
       
   370         init = false;
       
   371         defaultMediaIndex = -1;
       
   372         try {
       
   373             myURL =
       
   374                 new URL(uriStr.replaceFirst("ipp", "http"));
       
   375         } catch (Exception e) {
       
   376             IPPPrintService.debug_println(debugPrefix+
       
   377                                           " IPPPrintService, myURL="+
       
   378                                           myURL+" Exception= "+
       
   379                                           e);
       
   380             throw new IllegalArgumentException("invalid url");
       
   381         }
       
   382 
       
   383         isCupsPrinter = isCups;
       
   384         try {
       
   385             myURI =  new URI(uriStr);
       
   386             debug_println(debugPrefix+"IPPPrintService myURI : "+myURI);
       
   387         } catch (java.net.URISyntaxException e) {
       
   388             throw new IllegalArgumentException("invalid uri");
       
   389         }
       
   390     }
       
   391 
       
   392 
       
   393     /*
       
   394      * Initialize mediaSizeNames, mediaTrays and other attributes.
       
   395      * Media size/trays are initialized to non-null values, may be 0-length
       
   396      * array.
       
   397      * NOTE: Must be called from a synchronized block only.
       
   398      */
       
   399     private void initAttributes() {
       
   400         if (!init) {
       
   401             // init customMediaSizeNames
       
   402             customMediaSizeNames = new CustomMediaSizeName[0];
       
   403 
       
   404             if ((urlConnection = getIPPConnection(myURL)) == null) {
       
   405                 mediaSizeNames = new MediaSizeName[0];
       
   406                 mediaTrays = new MediaTray[0];
       
   407                 debug_println(debugPrefix+"initAttributes, NULL urlConnection ");
       
   408                 init = true;
       
   409                 return;
       
   410             }
       
   411 
       
   412             // get all supported attributes through IPP
       
   413             opGetAttributes();
       
   414 
       
   415             if (isCupsPrinter) {
       
   416                 // note, it is possible to query media in CUPS using IPP
       
   417                 // right now we always get it from PPD.
       
   418                 // maybe use "&& (usePPD)" later?
       
   419                 // Another reason why we use PPD is because
       
   420                 // IPP currently does not support it but PPD does.
       
   421 
       
   422                 try {
       
   423                     cps = new CUPSPrinter(printer);
       
   424                     mediaSizeNames = cps.getMediaSizeNames();
       
   425                     mediaTrays = cps.getMediaTrays();
       
   426                     customMediaSizeNames = cps.getCustomMediaSizeNames();
       
   427                     defaultMediaIndex = cps.getDefaultMediaIndex();
       
   428                     rawResolutions = cps.getRawResolutions();
       
   429                     urlConnection.disconnect();
       
   430                     init = true;
       
   431                     return;
       
   432                 } catch (Exception e) {
       
   433                     IPPPrintService.debug_println(debugPrefix+
       
   434                                        "initAttributes, error creating CUPSPrinter e="+e);
       
   435                 }
       
   436             }
       
   437 
       
   438             // use IPP to get all media,
       
   439             Media[] allMedia = getSupportedMedia();
       
   440             ArrayList<Media> sizeList = new ArrayList<>();
       
   441             ArrayList<Media> trayList = new ArrayList<>();
       
   442             for (int i=0; i<allMedia.length; i++) {
       
   443                 if (allMedia[i] instanceof MediaSizeName) {
       
   444                     sizeList.add(allMedia[i]);
       
   445                 } else if (allMedia[i] instanceof MediaTray) {
       
   446                     trayList.add(allMedia[i]);
       
   447                 }
       
   448             }
       
   449 
       
   450             if (sizeList != null) {
       
   451                 mediaSizeNames = new MediaSizeName[sizeList.size()];
       
   452                 mediaSizeNames = sizeList.toArray(mediaSizeNames);
       
   453             }
       
   454             if (trayList != null) {
       
   455                 mediaTrays = new MediaTray[trayList.size()];
       
   456                 mediaTrays = trayList.toArray(mediaTrays);
       
   457             }
       
   458             urlConnection.disconnect();
       
   459 
       
   460             init = true;
       
   461         }
       
   462     }
       
   463 
       
   464 
       
   465     public DocPrintJob createPrintJob() {
       
   466         SecurityManager security = System.getSecurityManager();
       
   467         if (security != null) {
       
   468             security.checkPrintJobAccess();
       
   469         }
       
   470         // REMIND: create IPPPrintJob
       
   471         return new UnixPrintJob(this);
       
   472     }
       
   473 
       
   474 
       
   475     public synchronized Object
       
   476         getSupportedAttributeValues(Class<? extends Attribute> category,
       
   477                                     DocFlavor flavor,
       
   478                                     AttributeSet attributes)
       
   479     {
       
   480         if (category == null) {
       
   481             throw new NullPointerException("null category");
       
   482         }
       
   483         if (!Attribute.class.isAssignableFrom(category)) {
       
   484             throw new IllegalArgumentException(category +
       
   485                                  " does not implement Attribute");
       
   486         }
       
   487         if (flavor != null) {
       
   488             if (!isDocFlavorSupported(flavor)) {
       
   489                 throw new IllegalArgumentException(flavor +
       
   490                                                " is an unsupported flavor");
       
   491             } else if (isAutoSense(flavor)) {
       
   492                 return null;
       
   493             }
       
   494 
       
   495         }
       
   496 
       
   497         if (!isAttributeCategorySupported(category)) {
       
   498             return null;
       
   499         }
       
   500 
       
   501         /* Test if the flavor is compatible with the attributes */
       
   502         if (!isDestinationSupported(flavor, attributes)) {
       
   503             return null;
       
   504         }
       
   505 
       
   506         initAttributes();
       
   507 
       
   508         /* Test if the flavor is compatible with the category */
       
   509         if ((category == Copies.class) ||
       
   510             (category == CopiesSupported.class)) {
       
   511             if (flavor == null ||
       
   512                 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
       
   513                   flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
       
   514                   flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) {
       
   515                 CopiesSupported cs = new CopiesSupported(1, MAXCOPIES);
       
   516                 AttributeClass attribClass = (getAttMap != null) ?
       
   517                     getAttMap.get(cs.getName()) : null;
       
   518                 if (attribClass != null) {
       
   519                     int[] range = attribClass.getIntRangeValue();
       
   520                     cs = new CopiesSupported(range[0], range[1]);
       
   521                 }
       
   522                 return cs;
       
   523             } else {
       
   524                 return null;
       
   525             }
       
   526         } else  if (category == Chromaticity.class) {
       
   527             if (flavor == null ||
       
   528                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
       
   529                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
       
   530                 !isIPPSupportedImages(flavor.getMimeType())) {
       
   531                 Chromaticity[]arr = new Chromaticity[1];
       
   532                 arr[0] = Chromaticity.COLOR;
       
   533                 return (arr);
       
   534             } else {
       
   535                 return null;
       
   536             }
       
   537         } else if (category == Destination.class) {
       
   538             if (flavor == null ||
       
   539                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
       
   540                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
       
   541                 try {
       
   542                     return new Destination((new File("out.ps")).toURI());
       
   543                 } catch (SecurityException se) {
       
   544                     try {
       
   545                         return new Destination(new URI("file:out.ps"));
       
   546                     } catch (URISyntaxException e) {
       
   547                         return null;
       
   548                     }
       
   549                 }
       
   550             }
       
   551             return null;
       
   552         } else if (category == Fidelity.class) {
       
   553             Fidelity []arr = new Fidelity[2];
       
   554             arr[0] = Fidelity.FIDELITY_FALSE;
       
   555             arr[1] = Fidelity.FIDELITY_TRUE;
       
   556             return arr;
       
   557         } else if (category == Finishings.class) {
       
   558             AttributeClass attribClass = (getAttMap != null) ?
       
   559                 getAttMap.get("finishings-supported")
       
   560                 : null;
       
   561             if (attribClass != null) {
       
   562                 int[] finArray = attribClass.getArrayOfIntValues();
       
   563                 if ((finArray != null) && (finArray.length > 0)) {
       
   564                     Finishings[] finSup = new Finishings[finArray.length];
       
   565                     for (int i=0; i<finArray.length; i++) {
       
   566                         finSup[i] = Finishings.NONE;
       
   567                         Finishings[] fAll = (Finishings[])
       
   568                             (new ExtFinishing(100)).getAll();
       
   569                         for (int j=0; j<fAll.length; j++) {
       
   570                             if (finArray[i] == fAll[j].getValue()) {
       
   571                                 finSup[i] = fAll[j];
       
   572                                 break;
       
   573                             }
       
   574                         }
       
   575                     }
       
   576                     return finSup;
       
   577                 }
       
   578             }
       
   579         } else if (category == JobName.class) {
       
   580             return new JobName("Java Printing", null);
       
   581         } else if (category == JobSheets.class) {
       
   582             JobSheets arr[] = new JobSheets[2];
       
   583             arr[0] = JobSheets.NONE;
       
   584             arr[1] = JobSheets.STANDARD;
       
   585             return arr;
       
   586 
       
   587         } else if (category == Media.class) {
       
   588             Media[] allMedia = new Media[mediaSizeNames.length+
       
   589                                         mediaTrays.length];
       
   590 
       
   591             for (int i=0; i<mediaSizeNames.length; i++) {
       
   592                 allMedia[i] = mediaSizeNames[i];
       
   593             }
       
   594 
       
   595             for (int i=0; i<mediaTrays.length; i++) {
       
   596                 allMedia[i+mediaSizeNames.length] = mediaTrays[i];
       
   597             }
       
   598 
       
   599             if (allMedia.length == 0) {
       
   600                 allMedia = new Media[1];
       
   601                 allMedia[0] = (Media)getDefaultAttributeValue(Media.class);
       
   602             }
       
   603 
       
   604             return allMedia;
       
   605         } else if (category == MediaPrintableArea.class) {
       
   606             MediaPrintableArea[] mpas = null;
       
   607             if (cps != null) {
       
   608                 mpas = cps.getMediaPrintableArea();
       
   609             }
       
   610 
       
   611             if (mpas == null) {
       
   612                 mpas = new MediaPrintableArea[1];
       
   613                 mpas[0] = (MediaPrintableArea)
       
   614                     getDefaultAttributeValue(MediaPrintableArea.class);
       
   615             }
       
   616 
       
   617             if ((attributes == null) || (attributes.size() == 0)) {
       
   618                 ArrayList<MediaPrintableArea> printableList =
       
   619                                        new ArrayList<MediaPrintableArea>();
       
   620 
       
   621                 for (int i=0; i<mpas.length; i++) {
       
   622                     if (mpas[i] != null) {
       
   623                         printableList.add(mpas[i]);
       
   624                     }
       
   625                 }
       
   626                 if (printableList.size() > 0) {
       
   627                     mpas  = new MediaPrintableArea[printableList.size()];
       
   628                     printableList.toArray(mpas);
       
   629                 }
       
   630                 return mpas;
       
   631             }
       
   632 
       
   633             int match = -1;
       
   634             Media media = (Media)attributes.get(Media.class);
       
   635             if (media != null && media instanceof MediaSizeName) {
       
   636                 MediaSizeName msn = (MediaSizeName)media;
       
   637 
       
   638                 // case when no supported mediasizenames are reported
       
   639                 // check given media against the default
       
   640                 if (mediaSizeNames.length == 0 &&
       
   641                     msn.equals(getDefaultAttributeValue(Media.class))) {
       
   642                     //default printable area is that of default mediasize
       
   643                     return mpas;
       
   644                 }
       
   645 
       
   646                 for (int i=0; i<mediaSizeNames.length; i++) {
       
   647                     if (msn.equals(mediaSizeNames[i])) {
       
   648                         match = i;
       
   649                     }
       
   650                 }
       
   651             }
       
   652 
       
   653             if (match == -1) {
       
   654                 return null;
       
   655             } else {
       
   656                 MediaPrintableArea []arr = new MediaPrintableArea[1];
       
   657                 arr[0] = mpas[match];
       
   658                 return arr;
       
   659             }
       
   660         } else if (category == NumberUp.class) {
       
   661             AttributeClass attribClass = (getAttMap != null) ?
       
   662                 getAttMap.get("number-up-supported") : null;
       
   663             if (attribClass != null) {
       
   664                 int[] values = attribClass.getArrayOfIntValues();
       
   665                 if (values != null) {
       
   666                     NumberUp[] nUp = new NumberUp[values.length];
       
   667                     for (int i=0; i<values.length; i++) {
       
   668                         nUp[i] = new NumberUp(values[i]);
       
   669                     }
       
   670                     return nUp;
       
   671                 } else {
       
   672                     return null;
       
   673                 }
       
   674             }
       
   675         } else if (category == OrientationRequested.class) {
       
   676             if ((flavor != null) &&
       
   677                 (flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
       
   678                  flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
       
   679                  flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) {
       
   680                 return null;
       
   681             }
       
   682 
       
   683             boolean revPort = false;
       
   684             OrientationRequested[] orientSup = null;
       
   685 
       
   686             AttributeClass attribClass = (getAttMap != null) ?
       
   687               getAttMap.get("orientation-requested-supported")
       
   688                 : null;
       
   689             if (attribClass != null) {
       
   690                 int[] orientArray = attribClass.getArrayOfIntValues();
       
   691                 if ((orientArray != null) && (orientArray.length > 0)) {
       
   692                     orientSup =
       
   693                         new OrientationRequested[orientArray.length];
       
   694                     for (int i=0; i<orientArray.length; i++) {
       
   695                         switch (orientArray[i]) {
       
   696                         default:
       
   697                         case 3 :
       
   698                             orientSup[i] = OrientationRequested.PORTRAIT;
       
   699                             break;
       
   700                         case 4:
       
   701                             orientSup[i] = OrientationRequested.LANDSCAPE;
       
   702                             break;
       
   703                         case 5:
       
   704                             orientSup[i] =
       
   705                                 OrientationRequested.REVERSE_LANDSCAPE;
       
   706                             break;
       
   707                         case 6:
       
   708                             orientSup[i] =
       
   709                                 OrientationRequested.REVERSE_PORTRAIT;
       
   710                             revPort = true;
       
   711                             break;
       
   712                         }
       
   713                     }
       
   714                 }
       
   715             }
       
   716             if (flavor == null ||
       
   717                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
       
   718                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
       
   719 
       
   720                 if (revPort && flavor == null) {
       
   721                     OrientationRequested []orSup = new OrientationRequested[4];
       
   722                     orSup[0] = OrientationRequested.PORTRAIT;
       
   723                     orSup[1] = OrientationRequested.LANDSCAPE;
       
   724                     orSup[2] = OrientationRequested.REVERSE_LANDSCAPE;
       
   725                     orSup[3] = OrientationRequested.REVERSE_PORTRAIT;
       
   726                     return orSup;
       
   727                 } else {
       
   728                     OrientationRequested []orSup = new OrientationRequested[3];
       
   729                     orSup[0] = OrientationRequested.PORTRAIT;
       
   730                     orSup[1] = OrientationRequested.LANDSCAPE;
       
   731                     orSup[2] = OrientationRequested.REVERSE_LANDSCAPE;
       
   732                     return orSup;
       
   733                 }
       
   734             } else {
       
   735                 return orientSup;
       
   736             }
       
   737         } else if (category == PageRanges.class) {
       
   738            if (flavor == null ||
       
   739                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
       
   740                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
       
   741                 PageRanges []arr = new PageRanges[1];
       
   742                 arr[0] = new PageRanges(1, Integer.MAX_VALUE);
       
   743                 return arr;
       
   744             } else {
       
   745                 // Returning null as this is not yet supported in UnixPrintJob.
       
   746                 return null;
       
   747             }
       
   748         } else if (category == RequestingUserName.class) {
       
   749             String userName = "";
       
   750             try {
       
   751               userName = System.getProperty("user.name", "");
       
   752             } catch (SecurityException se) {
       
   753             }
       
   754             return new RequestingUserName(userName, null);
       
   755         } else if (category == Sides.class) {
       
   756             // The printer takes care of Sides so if short-edge
       
   757             // is chosen in a job, the rotation is done by the printer.
       
   758             // Orientation is rotated by emulation if pageable
       
   759             // or printable so if the document is in Landscape, this may
       
   760             // result in double rotation.
       
   761             AttributeClass attribClass = (getAttMap != null) ?
       
   762                 getAttMap.get("sides-supported")
       
   763                 : null;
       
   764             if (attribClass != null) {
       
   765                 String[] sidesArray = attribClass.getArrayOfStringValues();
       
   766                 if ((sidesArray != null) && (sidesArray.length > 0)) {
       
   767                     Sides[] sidesSup = new Sides[sidesArray.length];
       
   768                     for (int i=0; i<sidesArray.length; i++) {
       
   769                         if (sidesArray[i].endsWith("long-edge")) {
       
   770                             sidesSup[i] = Sides.TWO_SIDED_LONG_EDGE;
       
   771                         } else if (sidesArray[i].endsWith("short-edge")) {
       
   772                             sidesSup[i] = Sides.TWO_SIDED_SHORT_EDGE;
       
   773                         } else {
       
   774                             sidesSup[i] = Sides.ONE_SIDED;
       
   775                         }
       
   776                     }
       
   777                     return sidesSup;
       
   778                 }
       
   779             }
       
   780         } else if (category == PrinterResolution.class) {
       
   781             PrinterResolution[] supportedRes = getPrintResolutions();
       
   782             if (supportedRes == null) {
       
   783                 return null;
       
   784             }
       
   785             PrinterResolution []arr =
       
   786                 new PrinterResolution[supportedRes.length];
       
   787             System.arraycopy(supportedRes, 0, arr, 0, supportedRes.length);
       
   788             return arr;
       
   789         }
       
   790 
       
   791         return null;
       
   792     }
       
   793 
       
   794     //This class is for getting all pre-defined Finishings
       
   795     @SuppressWarnings("serial") // JDK implementation class
       
   796     private class ExtFinishing extends Finishings {
       
   797         ExtFinishing(int value) {
       
   798             super(100); // 100 to avoid any conflicts with predefined values.
       
   799         }
       
   800 
       
   801         EnumSyntax[] getAll() {
       
   802             EnumSyntax[] es = super.getEnumValueTable();
       
   803             return es;
       
   804         }
       
   805     }
       
   806 
       
   807 
       
   808     public AttributeSet getUnsupportedAttributes(DocFlavor flavor,
       
   809                                                  AttributeSet attributes) {
       
   810         if (flavor != null && !isDocFlavorSupported(flavor)) {
       
   811             throw new IllegalArgumentException("flavor " + flavor +
       
   812                                                "is not supported");
       
   813         }
       
   814 
       
   815         if (attributes == null) {
       
   816             return null;
       
   817         }
       
   818 
       
   819         Attribute attr;
       
   820         AttributeSet unsupp = new HashAttributeSet();
       
   821         Attribute []attrs = attributes.toArray();
       
   822         for (int i=0; i<attrs.length; i++) {
       
   823             try {
       
   824                 attr = attrs[i];
       
   825                 if (!isAttributeCategorySupported(attr.getCategory())) {
       
   826                     unsupp.add(attr);
       
   827                 } else if (!isAttributeValueSupported(attr, flavor,
       
   828                                                       attributes)) {
       
   829                     unsupp.add(attr);
       
   830                 }
       
   831             } catch (ClassCastException e) {
       
   832             }
       
   833         }
       
   834         if (unsupp.isEmpty()) {
       
   835             return null;
       
   836         } else {
       
   837             return unsupp;
       
   838         }
       
   839     }
       
   840 
       
   841 
       
   842     public synchronized DocFlavor[] getSupportedDocFlavors() {
       
   843 
       
   844         if (supportedDocFlavors != null) {
       
   845             int len = supportedDocFlavors.length;
       
   846                 DocFlavor[] copyflavors = new DocFlavor[len];
       
   847                 System.arraycopy(supportedDocFlavors, 0, copyflavors, 0, len);
       
   848                 return copyflavors;
       
   849         }
       
   850         initAttributes();
       
   851 
       
   852         if ((getAttMap != null) &&
       
   853             getAttMap.containsKey("document-format-supported")) {
       
   854 
       
   855             AttributeClass attribClass =
       
   856                 getAttMap.get("document-format-supported");
       
   857             if (attribClass != null) {
       
   858                 String mimeType;
       
   859                 boolean psSupported = false;
       
   860                 String[] docFlavors = attribClass.getArrayOfStringValues();
       
   861                 DocFlavor[] flavors;
       
   862                 HashSet<Object> docList = new HashSet<>();
       
   863                 int j;
       
   864                 String hostEnc = DocFlavor.hostEncoding.
       
   865                     toLowerCase(Locale.ENGLISH);
       
   866                 boolean addHostEncoding = !hostEnc.equals("utf-8") &&
       
   867                     !hostEnc.equals("utf-16") && !hostEnc.equals("utf-16be") &&
       
   868                     !hostEnc.equals("utf-16le") && !hostEnc.equals("us-ascii");
       
   869 
       
   870                 for (int i = 0; i < docFlavors.length; i++) {
       
   871                     for (j=0; j<allDocFlavors.length; j++) {
       
   872                         flavors = (DocFlavor[])allDocFlavors[j];
       
   873 
       
   874                         mimeType = flavors[0].getMimeType();
       
   875                         if (mimeType.startsWith(docFlavors[i])) {
       
   876 
       
   877                             docList.addAll(Arrays.asList(flavors));
       
   878 
       
   879                             if (mimeType.equals("text/plain") &&
       
   880                                 addHostEncoding) {
       
   881                                 docList.add(Arrays.asList(textPlainHost));
       
   882                             } else if (mimeType.equals("text/html") &&
       
   883                                        addHostEncoding) {
       
   884                                 docList.add(Arrays.asList(textHtmlHost));
       
   885                             } else if (mimeType.equals("image/png")) {
       
   886                                 pngImagesAdded = true;
       
   887                             } else if (mimeType.equals("image/gif")) {
       
   888                                 gifImagesAdded = true;
       
   889                             } else if (mimeType.equals("image/jpeg")) {
       
   890                                 jpgImagesAdded = true;
       
   891                             } else if (mimeType.indexOf("postscript") != -1) {
       
   892                                 psSupported = true;
       
   893                             }
       
   894                             break;
       
   895                         }
       
   896                     }
       
   897 
       
   898                     // Not added? Create new DocFlavors
       
   899                     if (j == allDocFlavors.length) {
       
   900                         //  make new DocFlavors
       
   901                         docList.add(new DocFlavor.BYTE_ARRAY(docFlavors[i]));
       
   902                         docList.add(new DocFlavor.INPUT_STREAM(docFlavors[i]));
       
   903                         docList.add(new DocFlavor.URL(docFlavors[i]));
       
   904                     }
       
   905                 }
       
   906 
       
   907                 // check if we need to add image DocFlavors
       
   908                 // and Pageable/Printable flavors
       
   909                 if (psSupported || isCupsPrinter) {
       
   910                     /*
       
   911                      Always add Pageable and Printable for CUPS
       
   912                      since it uses Filters to convert from Postscript
       
   913                      to device printer language.
       
   914                     */
       
   915                     docList.add(DocFlavor.SERVICE_FORMATTED.PAGEABLE);
       
   916                     docList.add(DocFlavor.SERVICE_FORMATTED.PRINTABLE);
       
   917 
       
   918                     docList.addAll(Arrays.asList(imageJPG));
       
   919                     docList.addAll(Arrays.asList(imagePNG));
       
   920                     docList.addAll(Arrays.asList(imageGIF));
       
   921                 }
       
   922                 supportedDocFlavors = new DocFlavor[docList.size()];
       
   923                 docList.toArray(supportedDocFlavors);
       
   924                 int len = supportedDocFlavors.length;
       
   925                 DocFlavor[] copyflavors = new DocFlavor[len];
       
   926                 System.arraycopy(supportedDocFlavors, 0, copyflavors, 0, len);
       
   927                 return copyflavors;
       
   928             }
       
   929         }
       
   930         DocFlavor[] flavor = new DocFlavor[2];
       
   931         flavor[0] = DocFlavor.SERVICE_FORMATTED.PAGEABLE;
       
   932         flavor[1] = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
       
   933         supportedDocFlavors = flavor;
       
   934         return flavor;
       
   935     }
       
   936 
       
   937 
       
   938     public boolean isDocFlavorSupported(DocFlavor flavor) {
       
   939         if (supportedDocFlavors == null) {
       
   940             getSupportedDocFlavors();
       
   941         }
       
   942         if (supportedDocFlavors != null) {
       
   943             for (int f=0; f<supportedDocFlavors.length; f++) {
       
   944                 if (flavor.equals(supportedDocFlavors[f])) {
       
   945                     return true;
       
   946                 }
       
   947             }
       
   948         }
       
   949         return false;
       
   950     }
       
   951 
       
   952 
       
   953     /**
       
   954      * Finds matching CustomMediaSizeName of given media.
       
   955      */
       
   956     public CustomMediaSizeName findCustomMedia(MediaSizeName media) {
       
   957         if (customMediaSizeNames == null) {
       
   958             return null;
       
   959         }
       
   960         for (int i=0; i< customMediaSizeNames.length; i++) {
       
   961             CustomMediaSizeName custom = customMediaSizeNames[i];
       
   962             MediaSizeName msn = custom.getStandardMedia();
       
   963             if (media.equals(msn)) {
       
   964                 return customMediaSizeNames[i];
       
   965             }
       
   966         }
       
   967         return null;
       
   968     }
       
   969 
       
   970 
       
   971     /**
       
   972      * Returns the matching standard Media using string comparison of names.
       
   973      */
       
   974     private Media getIPPMedia(String mediaName) {
       
   975         CustomMediaSizeName sampleSize = new CustomMediaSizeName("sample", "",
       
   976                                                                  0, 0);
       
   977         Media[] sizes = sampleSize.getSuperEnumTable();
       
   978         for (int i=0; i<sizes.length; i++) {
       
   979             if (mediaName.equals(""+sizes[i])) {
       
   980                 return sizes[i];
       
   981             }
       
   982         }
       
   983         CustomMediaTray sampleTray = new CustomMediaTray("sample", "");
       
   984         Media[] trays = sampleTray.getSuperEnumTable();
       
   985         for (int i=0; i<trays.length; i++) {
       
   986             if (mediaName.equals(""+trays[i])) {
       
   987                 return trays[i];
       
   988             }
       
   989         }
       
   990         return null;
       
   991     }
       
   992 
       
   993     private Media[] getSupportedMedia() {
       
   994         if ((getAttMap != null) &&
       
   995             getAttMap.containsKey("media-supported")) {
       
   996 
       
   997             AttributeClass attribClass = getAttMap.get("media-supported");
       
   998 
       
   999             if (attribClass != null) {
       
  1000                 String[] mediaVals = attribClass.getArrayOfStringValues();
       
  1001                 Media msn;
       
  1002                 Media[] mediaNames =
       
  1003                     new Media[mediaVals.length];
       
  1004                 for (int i=0; i<mediaVals.length; i++) {
       
  1005                     msn = getIPPMedia(mediaVals[i]);
       
  1006                     //REMIND: if null, create custom?
       
  1007                     mediaNames[i] = msn;
       
  1008                 }
       
  1009                 return mediaNames;
       
  1010             }
       
  1011         }
       
  1012         return new Media[0];
       
  1013     }
       
  1014 
       
  1015 
       
  1016     public synchronized Class<?>[] getSupportedAttributeCategories() {
       
  1017         if (supportedCats != null) {
       
  1018             Class<?> [] copyCats = new Class<?>[supportedCats.length];
       
  1019             System.arraycopy(supportedCats, 0, copyCats, 0, copyCats.length);
       
  1020             return copyCats;
       
  1021         }
       
  1022 
       
  1023         initAttributes();
       
  1024 
       
  1025         ArrayList<Class<?>> catList = new ArrayList<>();
       
  1026 
       
  1027         for (int i=0; i < printReqAttribDefault.length; i++) {
       
  1028             PrintRequestAttribute pra =
       
  1029                 (PrintRequestAttribute)printReqAttribDefault[i];
       
  1030             if (getAttMap != null &&
       
  1031                 getAttMap.containsKey(pra.getName()+"-supported")) {
       
  1032                 catList.add(pra.getCategory());
       
  1033             }
       
  1034         }
       
  1035 
       
  1036         // Some IPP printers like lexc710 do not have list of supported media
       
  1037         // but CUPS can get the media from PPD, so we still report as
       
  1038         // supported category.
       
  1039         if (isCupsPrinter) {
       
  1040             if (!catList.contains(Media.class)) {
       
  1041                 catList.add(Media.class);
       
  1042             }
       
  1043 
       
  1044             // Always add MediaPrintable for cups,
       
  1045             // because we can get it from PPD.
       
  1046             catList.add(MediaPrintableArea.class);
       
  1047 
       
  1048             // this is already supported in UnixPrintJob
       
  1049             catList.add(Destination.class);
       
  1050 
       
  1051             // It is unfortunate that CUPS doesn't provide a way to query
       
  1052             // if printer supports collation but since most printers
       
  1053             // now supports collation and that most OS has a way
       
  1054             // of setting it, it is a safe assumption to just always
       
  1055             // include SheetCollate as supported attribute.
       
  1056 
       
  1057             catList.add(SheetCollate.class);
       
  1058 
       
  1059         }
       
  1060 
       
  1061         // With the assumption that  Chromaticity is equivalent to
       
  1062         // ColorSupported.
       
  1063         if (getAttMap != null && getAttMap.containsKey("color-supported")) {
       
  1064             catList.add(Chromaticity.class);
       
  1065         }
       
  1066 
       
  1067         // CUPS does not report printer resolution via IPP but it
       
  1068         // may be gleaned from the PPD.
       
  1069         PrinterResolution[] supportedRes = getPrintResolutions();
       
  1070         if (supportedRes != null && (supportedRes.length > 0)) {
       
  1071             catList.add(PrinterResolution.class);
       
  1072         }
       
  1073 
       
  1074         supportedCats = new Class<?>[catList.size()];
       
  1075         catList.toArray(supportedCats);
       
  1076         Class<?>[] copyCats = new Class<?>[supportedCats.length];
       
  1077         System.arraycopy(supportedCats, 0, copyCats, 0, copyCats.length);
       
  1078         return copyCats;
       
  1079     }
       
  1080 
       
  1081 
       
  1082     public boolean
       
  1083         isAttributeCategorySupported(Class<? extends Attribute> category)
       
  1084     {
       
  1085         if (category == null) {
       
  1086             throw new NullPointerException("null category");
       
  1087         }
       
  1088         if (!(Attribute.class.isAssignableFrom(category))) {
       
  1089             throw new IllegalArgumentException(category +
       
  1090                                              " is not an Attribute");
       
  1091         }
       
  1092 
       
  1093         if (supportedCats == null) {
       
  1094             getSupportedAttributeCategories();
       
  1095         }
       
  1096 
       
  1097         // It is safe to assume that Orientation is always supported
       
  1098         // and even if CUPS or an IPP device reports it as not,
       
  1099         // our renderer can do portrait, landscape and
       
  1100         // reverse landscape.
       
  1101         if (category == OrientationRequested.class) {
       
  1102             return true;
       
  1103         }
       
  1104 
       
  1105         for (int i=0;i<supportedCats.length;i++) {
       
  1106             if (category == supportedCats[i]) {
       
  1107                 return true;
       
  1108             }
       
  1109         }
       
  1110 
       
  1111         return false;
       
  1112     }
       
  1113 
       
  1114     @SuppressWarnings("unchecked")
       
  1115     public synchronized <T extends PrintServiceAttribute>
       
  1116         T getAttribute(Class<T> category)
       
  1117     {
       
  1118         if (category == null) {
       
  1119             throw new NullPointerException("category");
       
  1120         }
       
  1121         if (!(PrintServiceAttribute.class.isAssignableFrom(category))) {
       
  1122             throw new IllegalArgumentException("Not a PrintServiceAttribute");
       
  1123         }
       
  1124 
       
  1125         initAttributes();
       
  1126 
       
  1127         if (category == PrinterName.class) {
       
  1128             return (T)(new PrinterName(printer, null));
       
  1129         } else if (category == PrinterInfo.class) {
       
  1130             PrinterInfo pInfo = new PrinterInfo(printer, null);
       
  1131             AttributeClass ac = (getAttMap != null) ?
       
  1132                 getAttMap.get(pInfo.getName())
       
  1133                 : null;
       
  1134             if (ac != null) {
       
  1135                 return (T)(new PrinterInfo(ac.getStringValue(), null));
       
  1136             }
       
  1137             return (T)pInfo;
       
  1138         } else if (category == QueuedJobCount.class) {
       
  1139             QueuedJobCount qjc = new QueuedJobCount(0);
       
  1140             AttributeClass ac = (getAttMap != null) ?
       
  1141                 getAttMap.get(qjc.getName())
       
  1142                 : null;
       
  1143             if (ac != null) {
       
  1144                 qjc = new QueuedJobCount(ac.getIntValue());
       
  1145             }
       
  1146             return (T)qjc;
       
  1147         } else if (category == PrinterIsAcceptingJobs.class) {
       
  1148             PrinterIsAcceptingJobs accJob =
       
  1149                 PrinterIsAcceptingJobs.ACCEPTING_JOBS;
       
  1150             AttributeClass ac = (getAttMap != null) ?
       
  1151                 getAttMap.get(accJob.getName())
       
  1152                 : null;
       
  1153             if ((ac != null) && (ac.getByteValue() == 0)) {
       
  1154                 accJob = PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS;
       
  1155             }
       
  1156             return (T)accJob;
       
  1157         } else if (category == ColorSupported.class) {
       
  1158             ColorSupported cs = ColorSupported.SUPPORTED;
       
  1159             AttributeClass ac = (getAttMap != null) ?
       
  1160                 getAttMap.get(cs.getName())
       
  1161                 : null;
       
  1162             if ((ac != null) && (ac.getByteValue() == 0)) {
       
  1163                 cs = ColorSupported.NOT_SUPPORTED;
       
  1164             }
       
  1165             return (T)cs;
       
  1166         } else if (category == PDLOverrideSupported.class) {
       
  1167 
       
  1168             if (isCupsPrinter) {
       
  1169                 // Documented: For CUPS this will always be false
       
  1170                 return (T)PDLOverrideSupported.NOT_ATTEMPTED;
       
  1171             } else {
       
  1172                 // REMIND: check attribute values
       
  1173                 return (T)PDLOverrideSupported.NOT_ATTEMPTED;
       
  1174             }
       
  1175         } else if (category == PrinterURI.class) {
       
  1176             return (T)(new PrinterURI(myURI));
       
  1177         } else {
       
  1178             return null;
       
  1179         }
       
  1180     }
       
  1181 
       
  1182 
       
  1183     public synchronized PrintServiceAttributeSet getAttributes() {
       
  1184         // update getAttMap by sending again get-attributes IPP request
       
  1185         init = false;
       
  1186         initAttributes();
       
  1187 
       
  1188         HashPrintServiceAttributeSet attrs =
       
  1189             new HashPrintServiceAttributeSet();
       
  1190 
       
  1191         for (int i=0; i < serviceAttributes.length; i++) {
       
  1192             String name = (String)serviceAttributes[i][1];
       
  1193             if (getAttMap != null && getAttMap.containsKey(name)) {
       
  1194                 @SuppressWarnings("unchecked")
       
  1195                 Class<PrintServiceAttribute> c = (Class<PrintServiceAttribute>)serviceAttributes[i][0];
       
  1196                 PrintServiceAttribute psa = getAttribute(c);
       
  1197                 if (psa != null) {
       
  1198                     attrs.add(psa);
       
  1199                 }
       
  1200             }
       
  1201         }
       
  1202         return AttributeSetUtilities.unmodifiableView(attrs);
       
  1203     }
       
  1204 
       
  1205     public boolean isIPPSupportedImages(String mimeType) {
       
  1206         if (supportedDocFlavors == null) {
       
  1207             getSupportedDocFlavors();
       
  1208         }
       
  1209 
       
  1210         if (mimeType.equals("image/png") && pngImagesAdded) {
       
  1211             return true;
       
  1212         } else if (mimeType.equals("image/gif") && gifImagesAdded) {
       
  1213             return true;
       
  1214         } else if (mimeType.equals("image/jpeg") && jpgImagesAdded) {
       
  1215             return true;
       
  1216         }
       
  1217 
       
  1218         return false;
       
  1219     }
       
  1220 
       
  1221 
       
  1222     private boolean isSupportedCopies(Copies copies) {
       
  1223         CopiesSupported cs = (CopiesSupported)
       
  1224             getSupportedAttributeValues(Copies.class, null, null);
       
  1225         int[][] members = cs.getMembers();
       
  1226         int min, max;
       
  1227         if ((members.length > 0) && (members[0].length > 0)) {
       
  1228             min = members[0][0];
       
  1229             max = members[0][1];
       
  1230         } else {
       
  1231             min = 1;
       
  1232             max = MAXCOPIES;
       
  1233         }
       
  1234 
       
  1235         int value = copies.getValue();
       
  1236         return (value >= min && value <= max);
       
  1237     }
       
  1238 
       
  1239     private boolean isAutoSense(DocFlavor flavor) {
       
  1240         if (flavor.equals(DocFlavor.BYTE_ARRAY.AUTOSENSE) ||
       
  1241             flavor.equals(DocFlavor.INPUT_STREAM.AUTOSENSE) ||
       
  1242             flavor.equals(DocFlavor.URL.AUTOSENSE)) {
       
  1243             return true;
       
  1244         }
       
  1245         else {
       
  1246             return false;
       
  1247         }
       
  1248     }
       
  1249 
       
  1250     private synchronized boolean isSupportedMediaTray(MediaTray msn) {
       
  1251         initAttributes();
       
  1252 
       
  1253         if (mediaTrays != null) {
       
  1254             for (int i=0; i<mediaTrays.length; i++) {
       
  1255                if (msn.equals(mediaTrays[i])) {
       
  1256                     return true;
       
  1257                 }
       
  1258             }
       
  1259         }
       
  1260         return false;
       
  1261     }
       
  1262 
       
  1263     private synchronized boolean isSupportedMedia(MediaSizeName msn) {
       
  1264         initAttributes();
       
  1265 
       
  1266         if (msn.equals((Media)getDefaultAttributeValue(Media.class))) {
       
  1267             return true;
       
  1268         }
       
  1269         for (int i=0; i<mediaSizeNames.length; i++) {
       
  1270             debug_println(debugPrefix+"isSupportedMedia, mediaSizeNames[i] "+mediaSizeNames[i]);
       
  1271             if (msn.equals(mediaSizeNames[i])) {
       
  1272                 return true;
       
  1273             }
       
  1274         }
       
  1275         return false;
       
  1276     }
       
  1277 
       
  1278     /* Return false if flavor is not null, pageable, nor printable and
       
  1279      * Destination is part of attributes.
       
  1280      */
       
  1281     private boolean
       
  1282         isDestinationSupported(DocFlavor flavor, AttributeSet attributes) {
       
  1283 
       
  1284             if ((attributes != null) &&
       
  1285                     (attributes.get(Destination.class) != null) &&
       
  1286                     !(flavor == null ||
       
  1287                       flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
       
  1288                       flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
       
  1289                 return false;
       
  1290             }
       
  1291             return true;
       
  1292     }
       
  1293 
       
  1294 
       
  1295     public boolean isAttributeValueSupported(Attribute attr,
       
  1296                                              DocFlavor flavor,
       
  1297                                              AttributeSet attributes) {
       
  1298         if (attr == null) {
       
  1299             throw new NullPointerException("null attribute");
       
  1300         }
       
  1301         if (flavor != null) {
       
  1302             if (!isDocFlavorSupported(flavor)) {
       
  1303                 throw new IllegalArgumentException(flavor +
       
  1304                                                " is an unsupported flavor");
       
  1305             } else if (isAutoSense(flavor)) {
       
  1306                 return false;
       
  1307             }
       
  1308         }
       
  1309         Class<? extends Attribute> category = attr.getCategory();
       
  1310         if (!isAttributeCategorySupported(category)) {
       
  1311             return false;
       
  1312         }
       
  1313 
       
  1314         /* Test if the flavor is compatible with the attributes */
       
  1315         if (!isDestinationSupported(flavor, attributes)) {
       
  1316             return false;
       
  1317         }
       
  1318 
       
  1319         /* Test if the flavor is compatible with the category */
       
  1320         if (attr.getCategory() == Chromaticity.class) {
       
  1321             if ((flavor == null) ||
       
  1322                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
       
  1323                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
       
  1324                 !isIPPSupportedImages(flavor.getMimeType())) {
       
  1325                 return attr == Chromaticity.COLOR;
       
  1326             } else {
       
  1327                 return false;
       
  1328             }
       
  1329         } else if (attr.getCategory() == Copies.class) {
       
  1330             return (flavor == null ||
       
  1331                    !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
       
  1332                    flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
       
  1333                    flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) &&
       
  1334                 isSupportedCopies((Copies)attr);
       
  1335 
       
  1336         } else if (attr.getCategory() == Destination.class) {
       
  1337             if (flavor == null ||
       
  1338                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
       
  1339                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
       
  1340                 URI uri = ((Destination)attr).getURI();
       
  1341                 if ("file".equals(uri.getScheme()) &&
       
  1342                     !(uri.getSchemeSpecificPart().equals(""))) {
       
  1343                     return true;
       
  1344                 }
       
  1345             }
       
  1346             return false;
       
  1347         } else if (attr.getCategory() == Media.class) {
       
  1348             if (attr instanceof MediaSizeName) {
       
  1349                 return isSupportedMedia((MediaSizeName)attr);
       
  1350             }
       
  1351             if (attr instanceof MediaTray) {
       
  1352                 return isSupportedMediaTray((MediaTray)attr);
       
  1353             }
       
  1354         } else if (attr.getCategory() == PageRanges.class) {
       
  1355             if (flavor != null &&
       
  1356                 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
       
  1357                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
       
  1358                 return false;
       
  1359             }
       
  1360         } else if (attr.getCategory() == SheetCollate.class) {
       
  1361             if (flavor != null &&
       
  1362                 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
       
  1363                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
       
  1364                 return false;
       
  1365             }
       
  1366         } else if (attr.getCategory() == Sides.class) {
       
  1367             Sides[] sidesArray = (Sides[])getSupportedAttributeValues(
       
  1368                                           Sides.class,
       
  1369                                           flavor,
       
  1370                                           attributes);
       
  1371 
       
  1372             if (sidesArray != null) {
       
  1373                 for (int i=0; i<sidesArray.length; i++) {
       
  1374                     if (sidesArray[i] == (Sides)attr) {
       
  1375                         return true;
       
  1376                     }
       
  1377                 }
       
  1378             }
       
  1379             return false;
       
  1380         } else if (attr.getCategory() == OrientationRequested.class) {
       
  1381             OrientationRequested[] orientArray =
       
  1382                 (OrientationRequested[])getSupportedAttributeValues(
       
  1383                                           OrientationRequested.class,
       
  1384                                           flavor,
       
  1385                                           attributes);
       
  1386 
       
  1387             if (orientArray != null) {
       
  1388                 for (int i=0; i<orientArray.length; i++) {
       
  1389                     if (orientArray[i] == (OrientationRequested)attr) {
       
  1390                         return true;
       
  1391                     }
       
  1392                 }
       
  1393             }
       
  1394             return false;
       
  1395         } if (attr.getCategory() == PrinterResolution.class) {
       
  1396             if (attr instanceof PrinterResolution) {
       
  1397                 return isSupportedResolution((PrinterResolution)attr);
       
  1398             }
       
  1399         }
       
  1400         return true;
       
  1401     }
       
  1402 
       
  1403 
       
  1404     public synchronized Object
       
  1405         getDefaultAttributeValue(Class<? extends Attribute> category)
       
  1406     {
       
  1407         if (category == null) {
       
  1408             throw new NullPointerException("null category");
       
  1409         }
       
  1410         if (!Attribute.class.isAssignableFrom(category)) {
       
  1411             throw new IllegalArgumentException(category +
       
  1412                                              " is not an Attribute");
       
  1413         }
       
  1414         if (!isAttributeCategorySupported(category)) {
       
  1415             return null;
       
  1416         }
       
  1417 
       
  1418         initAttributes();
       
  1419 
       
  1420         String catName = null;
       
  1421         for (int i=0; i < printReqAttribDefault.length; i++) {
       
  1422             PrintRequestAttribute pra =
       
  1423                 (PrintRequestAttribute)printReqAttribDefault[i];
       
  1424             if (pra.getCategory() == category) {
       
  1425                 catName = pra.getName();
       
  1426                 break;
       
  1427             }
       
  1428         }
       
  1429         String attribName = catName+"-default";
       
  1430         AttributeClass attribClass = (getAttMap != null) ?
       
  1431                 getAttMap.get(attribName) : null;
       
  1432 
       
  1433         if (category == Copies.class) {
       
  1434             if (attribClass != null) {
       
  1435                 return new Copies(attribClass.getIntValue());
       
  1436             } else {
       
  1437                 return new Copies(1);
       
  1438             }
       
  1439         } else if (category == Chromaticity.class) {
       
  1440             return Chromaticity.COLOR;
       
  1441         } else if (category == Destination.class) {
       
  1442             try {
       
  1443                 return new Destination((new File("out.ps")).toURI());
       
  1444             } catch (SecurityException se) {
       
  1445                 try {
       
  1446                     return new Destination(new URI("file:out.ps"));
       
  1447                 } catch (URISyntaxException e) {
       
  1448                     return null;
       
  1449                 }
       
  1450             }
       
  1451         } else if (category == Fidelity.class) {
       
  1452             return Fidelity.FIDELITY_FALSE;
       
  1453         } else if (category == Finishings.class) {
       
  1454             return Finishings.NONE;
       
  1455         } else if (category == JobName.class) {
       
  1456             return new JobName("Java Printing", null);
       
  1457         } else if (category == JobSheets.class) {
       
  1458             if (attribClass != null &&
       
  1459                 attribClass.getStringValue().equals("none")) {
       
  1460                 return JobSheets.NONE;
       
  1461             } else {
       
  1462                 return JobSheets.STANDARD;
       
  1463             }
       
  1464         } else if (category == Media.class) {
       
  1465             if (defaultMediaIndex == -1) {
       
  1466                 defaultMediaIndex = 0;
       
  1467             }
       
  1468             if (mediaSizeNames.length == 0) {
       
  1469                 String defaultCountry = Locale.getDefault().getCountry();
       
  1470                 if (defaultCountry != null &&
       
  1471                     (defaultCountry.equals("") ||
       
  1472                      defaultCountry.equals(Locale.US.getCountry()) ||
       
  1473                      defaultCountry.equals(Locale.CANADA.getCountry()))) {
       
  1474                     return MediaSizeName.NA_LETTER;
       
  1475                 } else {
       
  1476                     return MediaSizeName.ISO_A4;
       
  1477                 }
       
  1478             }
       
  1479 
       
  1480             if (attribClass != null) {
       
  1481                 String name = attribClass.getStringValue();
       
  1482                 if (isCupsPrinter) {
       
  1483                     return mediaSizeNames[defaultMediaIndex];
       
  1484                 } else {
       
  1485                     for (int i=0; i< mediaSizeNames.length; i++) {
       
  1486                         if (mediaSizeNames[i].toString().indexOf(name) != -1) {
       
  1487                             defaultMediaIndex = i;
       
  1488                             return mediaSizeNames[defaultMediaIndex];
       
  1489                         }
       
  1490                     }
       
  1491                 }
       
  1492             }
       
  1493             return mediaSizeNames[defaultMediaIndex];
       
  1494 
       
  1495         } else if (category == MediaPrintableArea.class) {
       
  1496             MediaPrintableArea[] mpas;
       
  1497              if ((cps != null)  &&
       
  1498                  ((mpas = cps.getMediaPrintableArea()) != null)) {
       
  1499                  if (defaultMediaIndex == -1) {
       
  1500                      // initializes value of defaultMediaIndex
       
  1501                      getDefaultAttributeValue(Media.class);
       
  1502                  }
       
  1503                  return mpas[defaultMediaIndex];
       
  1504              } else {
       
  1505                  String defaultCountry = Locale.getDefault().getCountry();
       
  1506                  float iw, ih;
       
  1507                  if (defaultCountry != null &&
       
  1508                      (defaultCountry.equals("") ||
       
  1509                       defaultCountry.equals(Locale.US.getCountry()) ||
       
  1510                       defaultCountry.equals(Locale.CANADA.getCountry()))) {
       
  1511                      iw = MediaSize.NA.LETTER.getX(Size2DSyntax.INCH) - 0.5f;
       
  1512                      ih = MediaSize.NA.LETTER.getY(Size2DSyntax.INCH) - 0.5f;
       
  1513                  } else {
       
  1514                      iw = MediaSize.ISO.A4.getX(Size2DSyntax.INCH) - 0.5f;
       
  1515                      ih = MediaSize.ISO.A4.getY(Size2DSyntax.INCH) - 0.5f;
       
  1516                  }
       
  1517                  return new MediaPrintableArea(0.25f, 0.25f, iw, ih,
       
  1518                                                MediaPrintableArea.INCH);
       
  1519              }
       
  1520         } else if (category == NumberUp.class) {
       
  1521             return new NumberUp(1); // for CUPS this is always 1
       
  1522         } else if (category == OrientationRequested.class) {
       
  1523             if (attribClass != null) {
       
  1524                 switch (attribClass.getIntValue()) {
       
  1525                 default:
       
  1526                 case 3: return OrientationRequested.PORTRAIT;
       
  1527                 case 4: return OrientationRequested.LANDSCAPE;
       
  1528                 case 5: return OrientationRequested.REVERSE_LANDSCAPE;
       
  1529                 case 6: return OrientationRequested.REVERSE_PORTRAIT;
       
  1530                 }
       
  1531             } else {
       
  1532                 return OrientationRequested.PORTRAIT;
       
  1533             }
       
  1534         } else if (category == PageRanges.class) {
       
  1535             if (attribClass != null) {
       
  1536                 int[] range = attribClass.getIntRangeValue();
       
  1537                 return new PageRanges(range[0], range[1]);
       
  1538             } else {
       
  1539                 return new PageRanges(1, Integer.MAX_VALUE);
       
  1540             }
       
  1541         } else if (category == RequestingUserName.class) {
       
  1542             String userName = "";
       
  1543             try {
       
  1544               userName = System.getProperty("user.name", "");
       
  1545             } catch (SecurityException se) {
       
  1546             }
       
  1547             return new RequestingUserName(userName, null);
       
  1548         } else if (category == SheetCollate.class) {
       
  1549             return SheetCollate.UNCOLLATED;
       
  1550         } else if (category == Sides.class) {
       
  1551             if (attribClass != null) {
       
  1552                 if (attribClass.getStringValue().endsWith("long-edge")) {
       
  1553                     return Sides.TWO_SIDED_LONG_EDGE;
       
  1554                 } else if (attribClass.getStringValue().endsWith(
       
  1555                                                            "short-edge")) {
       
  1556                     return Sides.TWO_SIDED_SHORT_EDGE;
       
  1557                 }
       
  1558             }
       
  1559             return Sides.ONE_SIDED;
       
  1560         } else if (category == PrinterResolution.class) {
       
  1561              PrinterResolution[] supportedRes = getPrintResolutions();
       
  1562              if ((supportedRes != null) && (supportedRes.length > 0)) {
       
  1563                 return supportedRes[0];
       
  1564              } else {
       
  1565                  return new PrinterResolution(300, 300, PrinterResolution.DPI);
       
  1566              }
       
  1567         }
       
  1568 
       
  1569         return null;
       
  1570     }
       
  1571 
       
  1572     private PrinterResolution[] getPrintResolutions() {
       
  1573         if (printerResolutions == null) {
       
  1574             if (rawResolutions == null) {
       
  1575               printerResolutions = new PrinterResolution[0];
       
  1576             } else {
       
  1577                 int numRes = rawResolutions.length / 2;
       
  1578                 PrinterResolution[] pres = new PrinterResolution[numRes];
       
  1579                 for (int i=0; i < numRes; i++) {
       
  1580                     pres[i] =  new PrinterResolution(rawResolutions[i*2],
       
  1581                                                      rawResolutions[i*2+1],
       
  1582                                                      PrinterResolution.DPI);
       
  1583                 }
       
  1584                 printerResolutions = pres;
       
  1585             }
       
  1586         }
       
  1587         return printerResolutions;
       
  1588     }
       
  1589 
       
  1590     private boolean isSupportedResolution(PrinterResolution res) {
       
  1591         PrinterResolution[] supportedRes = getPrintResolutions();
       
  1592         if (supportedRes != null) {
       
  1593             for (int i=0; i<supportedRes.length; i++) {
       
  1594                 if (res.equals(supportedRes[i])) {
       
  1595                     return true;
       
  1596                 }
       
  1597             }
       
  1598         }
       
  1599         return false;
       
  1600     }
       
  1601 
       
  1602     public ServiceUIFactory getServiceUIFactory() {
       
  1603         return null;
       
  1604     }
       
  1605 
       
  1606     public void wakeNotifier() {
       
  1607         synchronized (this) {
       
  1608             if (notifier != null) {
       
  1609                 notifier.wake();
       
  1610             }
       
  1611         }
       
  1612     }
       
  1613 
       
  1614     public void addPrintServiceAttributeListener(
       
  1615                                  PrintServiceAttributeListener listener) {
       
  1616         synchronized (this) {
       
  1617             if (listener == null) {
       
  1618                 return;
       
  1619             }
       
  1620             if (notifier == null) {
       
  1621                 notifier = new ServiceNotifier(this);
       
  1622             }
       
  1623             notifier.addListener(listener);
       
  1624         }
       
  1625     }
       
  1626 
       
  1627     public void removePrintServiceAttributeListener(
       
  1628                                   PrintServiceAttributeListener listener) {
       
  1629         synchronized (this) {
       
  1630             if (listener == null || notifier == null ) {
       
  1631                 return;
       
  1632             }
       
  1633             notifier.removeListener(listener);
       
  1634             if (notifier.isEmpty()) {
       
  1635                 notifier.stopNotifier();
       
  1636                 notifier = null;
       
  1637             }
       
  1638         }
       
  1639     }
       
  1640 
       
  1641     String getDest() {
       
  1642         return printer;
       
  1643     }
       
  1644 
       
  1645     public String getName() {
       
  1646         /*
       
  1647          * Mac is using printer-info IPP attribute for its human-readable printer
       
  1648          * name and is also the identifier used in NSPrintInfo:setPrinter.
       
  1649          */
       
  1650         if (PrintServiceLookupProvider.isMac()) {
       
  1651             PrintServiceAttributeSet psaSet = this.getAttributes();
       
  1652             if (psaSet != null) {
       
  1653                 PrinterInfo pName = (PrinterInfo)psaSet.get(PrinterInfo.class);
       
  1654                 if (pName != null) {
       
  1655                     return pName.toString();
       
  1656                 }
       
  1657             }
       
  1658         }
       
  1659         return printer;
       
  1660     }
       
  1661 
       
  1662 
       
  1663     public boolean usesClass(Class<?> c) {
       
  1664         return (c == sun.print.PSPrinterJob.class);
       
  1665     }
       
  1666 
       
  1667 
       
  1668     public static HttpURLConnection getIPPConnection(URL url) {
       
  1669         HttpURLConnection connection;
       
  1670         URLConnection urlc;
       
  1671         try {
       
  1672             urlc = url.openConnection();
       
  1673         } catch (java.io.IOException ioe) {
       
  1674             return null;
       
  1675         }
       
  1676         if (!(urlc instanceof HttpURLConnection)) {
       
  1677             return null;
       
  1678         }
       
  1679         connection = (HttpURLConnection)urlc;
       
  1680         connection.setUseCaches(false);
       
  1681         connection.setDoInput(true);
       
  1682         connection.setDoOutput(true);
       
  1683         connection.setRequestProperty("Content-type", "application/ipp");
       
  1684         return connection;
       
  1685     }
       
  1686 
       
  1687 
       
  1688     public synchronized boolean isPostscript() {
       
  1689         if (isPS == null) {
       
  1690            isPS = Boolean.TRUE;
       
  1691             if (isCupsPrinter) {
       
  1692                 try {
       
  1693                     urlConnection = getIPPConnection(
       
  1694                                              new URL(myURL+".ppd"));
       
  1695 
       
  1696                    InputStream is = urlConnection.getInputStream();
       
  1697                    if (is != null) {
       
  1698                        BufferedReader d =
       
  1699                            new BufferedReader(new InputStreamReader(is,
       
  1700                                                           Charset.forName("ISO-8859-1")));
       
  1701                        String lineStr;
       
  1702                        while ((lineStr = d.readLine()) != null) {
       
  1703                            if (lineStr.startsWith("*cupsFilter:")) {
       
  1704                                isPS = Boolean.FALSE;
       
  1705                                break;
       
  1706                            }
       
  1707                        }
       
  1708                     }
       
  1709                 } catch (java.io.IOException e) {
       
  1710                     debug_println(" isPostscript, e= "+e);
       
  1711                     /* if PPD is not found, this may be a raw printer
       
  1712                        and in this case it is assumed that it is a
       
  1713                        Postscript printer */
       
  1714                     // do nothing
       
  1715                 }
       
  1716             }
       
  1717         }
       
  1718         return isPS.booleanValue();
       
  1719     }
       
  1720 
       
  1721 
       
  1722     private void opGetAttributes() {
       
  1723         try {
       
  1724             debug_println(debugPrefix+"opGetAttributes myURI "+myURI+" myURL "+myURL);
       
  1725 
       
  1726             AttributeClass attClNoUri[] = {
       
  1727                 AttributeClass.ATTRIBUTES_CHARSET,
       
  1728                 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE};
       
  1729 
       
  1730             AttributeClass attCl[] = {
       
  1731                 AttributeClass.ATTRIBUTES_CHARSET,
       
  1732                 AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE,
       
  1733                 new AttributeClass("printer-uri",
       
  1734                                    AttributeClass.TAG_URI,
       
  1735                                    ""+myURI)};
       
  1736 
       
  1737             OutputStream os = java.security.AccessController.
       
  1738                 doPrivileged(new java.security.PrivilegedAction<OutputStream>() {
       
  1739                     public OutputStream run() {
       
  1740                         try {
       
  1741                             return urlConnection.getOutputStream();
       
  1742                         } catch (Exception e) {
       
  1743                         }
       
  1744                         return null;
       
  1745                     }
       
  1746                 });
       
  1747 
       
  1748             if (os == null) {
       
  1749                 return;
       
  1750             }
       
  1751 
       
  1752             boolean success = (myURI == null) ?
       
  1753                 writeIPPRequest(os, OP_GET_ATTRIBUTES, attClNoUri) :
       
  1754                 writeIPPRequest(os, OP_GET_ATTRIBUTES, attCl);
       
  1755             if (success) {
       
  1756                 InputStream is = null;
       
  1757                 if ((is = urlConnection.getInputStream())!=null) {
       
  1758                     HashMap<String, AttributeClass>[] responseMap = readIPPResponse(is);
       
  1759 
       
  1760                     if (responseMap != null && responseMap.length > 0) {
       
  1761                         getAttMap = responseMap[0];
       
  1762                         // If there is extra hashmap created due to duplicate
       
  1763                         // key/attribute present in IPPresponse, then use that
       
  1764                         // map too by appending to getAttMap after removing the
       
  1765                         // duplicate key/value
       
  1766                         if (responseMap.length > 1) {
       
  1767                             for (int i = 1; i < responseMap.length; i++) {
       
  1768                                 for (Map.Entry<String, AttributeClass> entry : responseMap[i].entrySet()) {
       
  1769                                     if (!getAttMap.containsKey(entry.getValue())) {
       
  1770                                         getAttMap.put(entry.getKey(), entry.getValue());
       
  1771                                     }
       
  1772                                 }
       
  1773                             }
       
  1774                         }
       
  1775                     }
       
  1776                 } else {
       
  1777                     debug_println(debugPrefix+"opGetAttributes - null input stream");
       
  1778                 }
       
  1779                 is.close();
       
  1780             }
       
  1781             os.close();
       
  1782         } catch (java.io.IOException e) {
       
  1783             debug_println(debugPrefix+"opGetAttributes - input/output stream: "+e);
       
  1784         }
       
  1785     }
       
  1786 
       
  1787 
       
  1788     public static boolean writeIPPRequest(OutputStream os,
       
  1789                                            String operCode,
       
  1790                                            AttributeClass[] attCl) {
       
  1791         OutputStreamWriter osw;
       
  1792         try {
       
  1793             osw = new OutputStreamWriter(os, "UTF-8");
       
  1794         } catch (java.io.UnsupportedEncodingException exc) {
       
  1795             debug_println(debugPrefix+"writeIPPRequest, UTF-8 not supported? Exception: "+exc);
       
  1796             return false;
       
  1797         }
       
  1798         debug_println(debugPrefix+"writeIPPRequest, op code= "+operCode);
       
  1799         char[] opCode =  new char[2];
       
  1800         opCode[0] =  (char)Byte.parseByte(operCode.substring(0,2), 16);
       
  1801         opCode[1] =  (char)Byte.parseByte(operCode.substring(2,4), 16);
       
  1802         char[] bytes = {0x01, 0x01, 0x00, 0x01};
       
  1803         try {
       
  1804             osw.write(bytes, 0, 2); // version number
       
  1805             osw.write(opCode, 0, 2); // operation code
       
  1806             bytes[0] = 0x00; bytes[1] = 0x00;
       
  1807             osw.write(bytes, 0, 4); // request ID #1
       
  1808 
       
  1809             bytes[0] = 0x01; // operation-group-tag
       
  1810             osw.write(bytes[0]);
       
  1811 
       
  1812             String valStr;
       
  1813             char[] lenStr;
       
  1814 
       
  1815             AttributeClass ac;
       
  1816             for (int i=0; i < attCl.length; i++) {
       
  1817                 ac = attCl[i];
       
  1818                 osw.write(ac.getType()); // value tag
       
  1819 
       
  1820                 lenStr = ac.getLenChars();
       
  1821                 osw.write(lenStr, 0, 2); // length
       
  1822                 osw.write(""+ac, 0, ac.getName().length());
       
  1823 
       
  1824                 // check if string range (0x35 -> 0x49)
       
  1825                 if (ac.getType() >= AttributeClass.TAG_TEXT_LANGUAGE &&
       
  1826                     ac.getType() <= AttributeClass.TAG_MIME_MEDIATYPE){
       
  1827                     valStr = (String)ac.getObjectValue();
       
  1828                     bytes[0] = 0; bytes[1] = (char)valStr.length();
       
  1829                     osw.write(bytes, 0, 2);
       
  1830                     osw.write(valStr, 0, valStr.length());
       
  1831                 } // REMIND: need to support other value tags but for CUPS
       
  1832                 // string is all we need.
       
  1833             }
       
  1834 
       
  1835             osw.write(GRPTAG_END_ATTRIBUTES);
       
  1836             osw.flush();
       
  1837             osw.close();
       
  1838         } catch (java.io.IOException ioe) {
       
  1839             debug_println(debugPrefix+"writeIPPRequest, IPPPrintService Exception in writeIPPRequest: "+ioe);
       
  1840             return false;
       
  1841         }
       
  1842         return true;
       
  1843     }
       
  1844 
       
  1845 
       
  1846     public static HashMap<String, AttributeClass>[] readIPPResponse(InputStream inputStream) {
       
  1847 
       
  1848         if (inputStream == null) {
       
  1849             return null;
       
  1850         }
       
  1851 
       
  1852         byte response[] = new byte[MAX_ATTRIBUTE_LENGTH];
       
  1853         try {
       
  1854 
       
  1855             DataInputStream ois = new DataInputStream(inputStream);
       
  1856 
       
  1857             // read status and ID
       
  1858             if ((ois.read(response, 0, 8) > -1) &&
       
  1859                 (response[2] == STATUSCODE_SUCCESS)) {
       
  1860 
       
  1861                 ByteArrayOutputStream outObj;
       
  1862                 int counter=0;
       
  1863                 short len = 0;
       
  1864                 String attribStr = null;
       
  1865                 // assign default value
       
  1866                 byte valTagByte = AttributeClass.TAG_KEYWORD;
       
  1867                 ArrayList<HashMap<String, AttributeClass>> respList = new ArrayList<>();
       
  1868                 HashMap<String, AttributeClass> responseMap = new HashMap<>();
       
  1869 
       
  1870                 response[0] = ois.readByte();
       
  1871 
       
  1872                 // check for group tags
       
  1873                 while ((response[0] >= GRPTAG_OP_ATTRIBUTES) &&
       
  1874                        (response[0] <= GRPTAG_PRINTER_ATTRIBUTES)
       
  1875                           && (response[0] != GRPTAG_END_ATTRIBUTES)) {
       
  1876                     debug_println(debugPrefix+"readIPPResponse, checking group tag,  response[0]= "+
       
  1877                                   response[0]);
       
  1878 
       
  1879                     outObj = new ByteArrayOutputStream();
       
  1880                     //make sure counter and attribStr are re-initialized
       
  1881                     counter = 0;
       
  1882                     attribStr = null;
       
  1883 
       
  1884                     // read value tag
       
  1885                     response[0] = ois.readByte();
       
  1886                     while (response[0] >= AttributeClass.TAG_UNSUPPORTED_VALUE &&
       
  1887                            response[0] <= AttributeClass.TAG_MEMBER_ATTRNAME) {
       
  1888                         // read name length
       
  1889                         len  = ois.readShort();
       
  1890 
       
  1891                         // If current value is not part of previous attribute
       
  1892                         // then close stream and add it to HashMap.
       
  1893                         // It is part of previous attribute if name length=0.
       
  1894                         if ((len != 0) && (attribStr != null)) {
       
  1895                             //last byte is the total # of values
       
  1896                             outObj.write(counter);
       
  1897                             outObj.flush();
       
  1898                             outObj.close();
       
  1899                             byte outArray[] = outObj.toByteArray();
       
  1900 
       
  1901                             // if key exists, new HashMap
       
  1902                             if (responseMap.containsKey(attribStr)) {
       
  1903                                 respList.add(responseMap);
       
  1904                                 responseMap = new HashMap<>();
       
  1905                             }
       
  1906 
       
  1907                             // exclude those that are unknown
       
  1908                             if (valTagByte >= AttributeClass.TAG_INT) {
       
  1909                                 AttributeClass ac =
       
  1910                                     new AttributeClass(attribStr,
       
  1911                                                        valTagByte,
       
  1912                                                        outArray);
       
  1913 
       
  1914                                 responseMap.put(ac.getName(), ac);
       
  1915                                 debug_println(debugPrefix+ "readIPPResponse "+ac);
       
  1916                             }
       
  1917 
       
  1918                             outObj = new ByteArrayOutputStream();
       
  1919                             counter = 0; //reset counter
       
  1920                         }
       
  1921                         //check if this is new value tag
       
  1922                         if (counter == 0) {
       
  1923                             valTagByte = response[0];
       
  1924                         }
       
  1925                         // read attribute name
       
  1926                         if (len != 0) {
       
  1927                             // read "len" characters
       
  1928                             // make sure it doesn't exceed the maximum
       
  1929                             if (len > MAX_ATTRIBUTE_LENGTH) {
       
  1930                                 response = new byte[len]; // expand as needed
       
  1931                             }
       
  1932                             ois.read(response, 0, len);
       
  1933                             attribStr = new String(response, 0, len);
       
  1934                         }
       
  1935                         // read value length
       
  1936                         len  = ois.readShort();
       
  1937                         // write name length
       
  1938                         outObj.write(len);
       
  1939                         // read value, make sure it doesn't exceed the maximum
       
  1940                         if (len > MAX_ATTRIBUTE_LENGTH) {
       
  1941                             response = new byte[len]; // expand as needed
       
  1942                         }
       
  1943                         ois.read(response, 0, len);
       
  1944                         // write value of "len" length
       
  1945                         outObj.write(response, 0, len);
       
  1946                         counter++;
       
  1947                         // read next byte
       
  1948                         response[0] = ois.readByte();
       
  1949                     }
       
  1950 
       
  1951                     if (attribStr != null) {
       
  1952                         outObj.write(counter);
       
  1953                         outObj.flush();
       
  1954                         outObj.close();
       
  1955 
       
  1956                         // if key exists in old HashMap, new HashMap
       
  1957                         if ((counter != 0) &&
       
  1958                             responseMap.containsKey(attribStr)) {
       
  1959                             respList.add(responseMap);
       
  1960                             responseMap = new HashMap<>();
       
  1961                         }
       
  1962 
       
  1963                         byte outArray[] = outObj.toByteArray();
       
  1964 
       
  1965                         AttributeClass ac =
       
  1966                             new AttributeClass(attribStr,
       
  1967                                                valTagByte,
       
  1968                                                outArray);
       
  1969                         responseMap.put(ac.getName(), ac);
       
  1970                     }
       
  1971                 }
       
  1972                 ois.close();
       
  1973                 if ((responseMap != null) && (responseMap.size() > 0)) {
       
  1974                     respList.add(responseMap);
       
  1975                 }
       
  1976                 @SuppressWarnings({"unchecked", "rawtypes"})
       
  1977                 HashMap<String, AttributeClass>[] tmp  =
       
  1978                     respList.toArray((HashMap<String, AttributeClass>[])new HashMap[respList.size()]);
       
  1979                 return tmp;
       
  1980             } else {
       
  1981                 debug_println(debugPrefix+
       
  1982                           "readIPPResponse client error, IPP status code: 0x"+
       
  1983                           toHex(response[2]) + toHex(response[3]));
       
  1984                 return null;
       
  1985             }
       
  1986 
       
  1987         } catch (java.io.IOException e) {
       
  1988             debug_println(debugPrefix+"readIPPResponse: "+e);
       
  1989             if (debugPrint) {
       
  1990                 e.printStackTrace();
       
  1991             }
       
  1992             return null;
       
  1993         }
       
  1994     }
       
  1995 
       
  1996     private static String toHex(byte v) {
       
  1997         String s = Integer.toHexString(v&0xff);
       
  1998         return (s.length() == 2) ? s :  "0"+s;
       
  1999     }
       
  2000 
       
  2001     public String toString() {
       
  2002         return "IPP Printer : " + getName();
       
  2003     }
       
  2004 
       
  2005     public boolean equals(Object obj) {
       
  2006         return  (obj == this ||
       
  2007                  (obj instanceof IPPPrintService &&
       
  2008                   ((IPPPrintService)obj).getName().equals(getName())));
       
  2009     }
       
  2010 
       
  2011     public int hashCode() {
       
  2012         return this.getClass().hashCode()+getName().hashCode();
       
  2013     }
       
  2014 }