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