1 /* 2 * Copyright (c) 2000, 2007, 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.io.File; 29 import java.net.URI; 30 import java.net.URISyntaxException; 31 import java.util.ArrayList; 32 import java.util.Locale; 33 34 import javax.print.DocFlavor; 35 import javax.print.DocPrintJob; 36 import javax.print.PrintService; 37 import javax.print.ServiceUIFactory; 38 import javax.print.attribute.Attribute; 39 import javax.print.attribute.AttributeSet; 40 import javax.print.attribute.AttributeSetUtilities; 41 import javax.print.attribute.HashAttributeSet; 42 import javax.print.attribute.PrintServiceAttribute; 43 import javax.print.attribute.PrintServiceAttributeSet; 44 import javax.print.attribute.HashPrintServiceAttributeSet; 45 import javax.print.attribute.Size2DSyntax; 46 import javax.print.attribute.standard.PrinterName; 47 import javax.print.attribute.standard.PrinterIsAcceptingJobs; 48 import javax.print.attribute.standard.QueuedJobCount; 49 import javax.print.attribute.standard.JobName; 50 import javax.print.attribute.standard.JobSheets; 51 import javax.print.attribute.standard.RequestingUserName; 52 import javax.print.attribute.standard.Chromaticity; 53 import javax.print.attribute.standard.ColorSupported; 54 import javax.print.attribute.standard.Copies; 55 import javax.print.attribute.standard.CopiesSupported; 56 import javax.print.attribute.standard.Destination; 57 import javax.print.attribute.standard.Fidelity; 58 import javax.print.attribute.standard.Media; 59 import javax.print.attribute.standard.MediaPrintableArea; 60 import javax.print.attribute.standard.MediaSize; 61 import javax.print.attribute.standard.MediaSizeName; 62 import javax.print.attribute.standard.OrientationRequested; 63 import javax.print.attribute.standard.PageRanges; 64 import javax.print.attribute.standard.PrinterState; 65 import javax.print.attribute.standard.PrinterStateReason; 66 import javax.print.attribute.standard.PrinterStateReasons; 67 import javax.print.attribute.standard.Severity; 68 import javax.print.attribute.standard.SheetCollate; 69 import javax.print.attribute.standard.Sides; 70 import javax.print.event.PrintServiceAttributeListener; 71 72 73 public class UnixPrintService implements PrintService, AttributeUpdater, 74 SunPrinterJobService { 75 76 /* define doc flavors for text types in the default encoding of 77 * this platform since we can always read those. 78 */ 79 private static String encoding = "ISO8859_1"; 80 private static DocFlavor textByteFlavor; 81 82 private static DocFlavor[] supportedDocFlavors = null; 83 private static final DocFlavor[] supportedDocFlavorsInit = { 84 DocFlavor.BYTE_ARRAY.POSTSCRIPT, 85 DocFlavor.INPUT_STREAM.POSTSCRIPT, 86 DocFlavor.URL.POSTSCRIPT, 87 DocFlavor.BYTE_ARRAY.GIF, 88 DocFlavor.INPUT_STREAM.GIF, 89 DocFlavor.URL.GIF, 90 DocFlavor.BYTE_ARRAY.JPEG, 91 DocFlavor.INPUT_STREAM.JPEG, 92 DocFlavor.URL.JPEG, 93 DocFlavor.BYTE_ARRAY.PNG, 94 DocFlavor.INPUT_STREAM.PNG, 95 DocFlavor.URL.PNG, 96 97 DocFlavor.CHAR_ARRAY.TEXT_PLAIN, 98 DocFlavor.READER.TEXT_PLAIN, 99 DocFlavor.STRING.TEXT_PLAIN, 100 101 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_8, 102 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16, 103 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16BE, 104 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_UTF_16LE, 105 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_US_ASCII, 106 107 108 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_8, 109 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16, 110 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16BE, 111 DocFlavor.INPUT_STREAM.TEXT_PLAIN_UTF_16LE, 112 DocFlavor.INPUT_STREAM.TEXT_PLAIN_US_ASCII, 113 114 115 DocFlavor.URL.TEXT_PLAIN_UTF_8, 116 DocFlavor.URL.TEXT_PLAIN_UTF_16, 117 DocFlavor.URL.TEXT_PLAIN_UTF_16BE, 118 DocFlavor.URL.TEXT_PLAIN_UTF_16LE, 119 DocFlavor.URL.TEXT_PLAIN_US_ASCII, 120 121 DocFlavor.SERVICE_FORMATTED.PAGEABLE, 122 DocFlavor.SERVICE_FORMATTED.PRINTABLE, 123 124 DocFlavor.BYTE_ARRAY.AUTOSENSE, 125 DocFlavor.URL.AUTOSENSE, 126 DocFlavor.INPUT_STREAM.AUTOSENSE 127 }; 128 129 private static final DocFlavor[] supportedHostDocFlavors = { 130 DocFlavor.BYTE_ARRAY.TEXT_PLAIN_HOST, 131 DocFlavor.INPUT_STREAM.TEXT_PLAIN_HOST, 132 DocFlavor.URL.TEXT_PLAIN_HOST 133 }; 134 135 String[] lpcStatusCom = { 136 "", 137 "| grep -E '^[ 0-9a-zA-Z_-]*@' | awk '{print $2, $3}'" 138 }; 139 140 String[] lpcQueueCom = { 141 "", 142 "| grep -E '^[ 0-9a-zA-Z_-]*@' | awk '{print $4}'" 143 }; 144 145 static { 146 encoding = java.security.AccessController.doPrivileged( 147 new sun.security.action.GetPropertyAction("file.encoding")); 148 } 149 150 /* let's try to support a few of these */ 151 private static final Class<?>[] serviceAttrCats = { 152 PrinterName.class, 153 PrinterIsAcceptingJobs.class, 154 QueuedJobCount.class, 155 }; 156 157 /* it turns out to be inconvenient to store the other categories 158 * separately because many attributes are in multiple categories. 159 */ 160 private static final Class<?>[] otherAttrCats = { 161 Chromaticity.class, 162 Copies.class, 163 Destination.class, 164 Fidelity.class, 165 JobName.class, 166 JobSheets.class, 167 Media.class, /* have to support this somehow ... */ 168 MediaPrintableArea.class, 169 OrientationRequested.class, 170 PageRanges.class, 171 RequestingUserName.class, 172 SheetCollate.class, 173 Sides.class, 174 }; 175 176 private static int MAXCOPIES = 1000; 177 178 private static final MediaSizeName mediaSizes[] = { 179 MediaSizeName.NA_LETTER, 180 MediaSizeName.TABLOID, 181 MediaSizeName.LEDGER, 182 MediaSizeName.NA_LEGAL, 183 MediaSizeName.EXECUTIVE, 184 MediaSizeName.ISO_A3, 185 MediaSizeName.ISO_A4, 186 MediaSizeName.ISO_A5, 187 MediaSizeName.ISO_B4, 188 MediaSizeName.ISO_B5, 189 }; 190 191 private String printer; 192 private PrinterName name; 193 private boolean isInvalid; 194 195 transient private PrintServiceAttributeSet lastSet; 196 transient private ServiceNotifier notifier = null; 197 198 UnixPrintService(String name) { 199 if (name == null) { 200 throw new IllegalArgumentException("null printer name"); 201 } 202 printer = name; 203 isInvalid = false; 204 } 205 206 public void invalidateService() { 207 isInvalid = true; 208 } 209 210 public String getName() { 211 return printer; 212 } 213 214 private PrinterName getPrinterName() { 215 if (name == null) { 216 name = new PrinterName(printer, null); 217 } 218 return name; 219 } 220 221 private PrinterIsAcceptingJobs getPrinterIsAcceptingJobsSysV() { 222 String command = "/usr/bin/lpstat -a " + printer; 223 String results[]= UnixPrintServiceLookup.execCmd(command); 224 225 if (results != null && results.length > 0) { 226 if (results[0].startsWith(printer + " accepting requests")) { 227 return PrinterIsAcceptingJobs.ACCEPTING_JOBS; 228 } 229 else if (results[0].startsWith(printer)) { 230 /* As well as "myprinter accepting requests", look for 231 * "myprinter@somehost accepting requests". 232 */ 233 int index = printer.length(); 234 String str = results[0]; 235 if (str.length() > index && 236 str.charAt(index) == '@' && 237 str.indexOf(" accepting requests", index) > 0 && 238 str.indexOf(" not accepting requests", index) == -1) { 239 return PrinterIsAcceptingJobs.ACCEPTING_JOBS; 240 } 241 } 242 } 243 return PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS ; 244 } 245 246 private PrinterIsAcceptingJobs getPrinterIsAcceptingJobsBSD() { 247 if (UnixPrintServiceLookup.cmdIndex == 248 UnixPrintServiceLookup.UNINITIALIZED) { 249 250 UnixPrintServiceLookup.cmdIndex = 251 UnixPrintServiceLookup.getBSDCommandIndex(); 252 } 253 254 String command = "/usr/sbin/lpc status " + printer 255 + lpcStatusCom[UnixPrintServiceLookup.cmdIndex]; 256 String results[]= UnixPrintServiceLookup.execCmd(command); 257 258 if (results != null && results.length > 0) { 259 if (UnixPrintServiceLookup.cmdIndex == 260 UnixPrintServiceLookup.BSD_LPD_NG) { 261 if (results[0].startsWith("enabled enabled")) { 262 return PrinterIsAcceptingJobs.ACCEPTING_JOBS ; 263 } 264 } else { 265 if ((results[1].trim().startsWith("queuing is enabled") && 266 results[2].trim().startsWith("printing is enabled")) || 267 (results.length >= 4 && 268 results[2].trim().startsWith("queuing is enabled") && 269 results[3].trim().startsWith("printing is enabled"))) { 270 return PrinterIsAcceptingJobs.ACCEPTING_JOBS ; 271 } 272 } 273 } 274 return PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS ; 275 } 276 277 // Filter the list of possible AIX Printers and remove header lines 278 // and extra lines which have been added for remote printers. 279 // 'protected' because this method is also used from UnixPrintServiceLookup. 280 protected static String[] filterPrinterNamesAIX(String[] posPrinters) { 281 ArrayList<String> printers = new ArrayList<>(); 282 String [] splitPart; 283 284 for(int i = 0; i < posPrinters.length; i++) { 285 // Remove the header lines 286 if (posPrinters[i].startsWith("---") || 287 posPrinters[i].startsWith("Queue") || 288 posPrinters[i].equals("")) continue; 289 290 // Check if there is a ":" in the end of the first colomn. 291 // This means that it is not a valid printer definition. 292 splitPart = posPrinters[i].split(" "); 293 if(splitPart.length >= 1 && !splitPart[0].trim().endsWith(":")) { 294 printers.add(posPrinters[i]); 295 } 296 } 297 298 return printers.toArray(new String[printers.size()]); 299 } 300 301 private PrinterIsAcceptingJobs getPrinterIsAcceptingJobsAIX() { 302 // On AIX there should not be a blank after '-a'. 303 String command = "/usr/bin/lpstat -a" + printer; 304 String results[]= UnixPrintServiceLookup.execCmd(command); 305 306 // Remove headers and bogus entries added by remote printers. 307 results = filterPrinterNamesAIX(results); 308 309 if (results != null && results.length > 0) { 310 for (int i = 0; i < results.length; i++) { 311 if (results[i].contains("READY") || 312 results[i].contains("RUNNING")) { 313 return PrinterIsAcceptingJobs.ACCEPTING_JOBS; 314 } 315 } 316 } 317 318 return PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS; 319 320 } 321 322 private PrinterIsAcceptingJobs getPrinterIsAcceptingJobs() { 323 if (UnixPrintServiceLookup.isSysV()) { 324 return getPrinterIsAcceptingJobsSysV(); 325 } else if (UnixPrintServiceLookup.isBSD()) { 326 return getPrinterIsAcceptingJobsBSD(); 327 } else if (UnixPrintServiceLookup.isAIX()) { 328 return getPrinterIsAcceptingJobsAIX(); 329 } else { 330 return PrinterIsAcceptingJobs.ACCEPTING_JOBS; 331 } 332 } 333 334 private PrinterState getPrinterState() { 335 if (isInvalid) { 336 return PrinterState.STOPPED; 337 } else { 338 return null; 339 } 340 } 341 342 private PrinterStateReasons getPrinterStateReasons() { 343 if (isInvalid) { 344 PrinterStateReasons psr = new PrinterStateReasons(); 345 psr.put(PrinterStateReason.SHUTDOWN, Severity.ERROR); 346 return psr; 347 } else { 348 return null; 349 } 350 } 351 352 private QueuedJobCount getQueuedJobCountSysV() { 353 String command = "/usr/bin/lpstat -R " + printer; 354 String results[]= UnixPrintServiceLookup.execCmd(command); 355 int qlen = (results == null) ? 0 : results.length; 356 357 return new QueuedJobCount(qlen); 358 } 359 360 private QueuedJobCount getQueuedJobCountBSD() { 361 if (UnixPrintServiceLookup.cmdIndex == 362 UnixPrintServiceLookup.UNINITIALIZED) { 363 364 UnixPrintServiceLookup.cmdIndex = 365 UnixPrintServiceLookup.getBSDCommandIndex(); 366 } 367 368 int qlen = 0; 369 String command = "/usr/sbin/lpc status " + printer 370 + lpcQueueCom[UnixPrintServiceLookup.cmdIndex]; 371 String results[] = UnixPrintServiceLookup.execCmd(command); 372 373 if (results != null && results.length > 0) { 374 String queued; 375 if (UnixPrintServiceLookup.cmdIndex == 376 UnixPrintServiceLookup.BSD_LPD_NG) { 377 queued = results[0]; 378 } else { 379 queued = results[3].trim(); 380 if (queued.startsWith("no")) { 381 return new QueuedJobCount(0); 382 } else { 383 queued = queued.substring(0, queued.indexOf(' ')); 384 } 385 } 386 387 try { 388 qlen = Integer.parseInt(queued); 389 } catch (NumberFormatException e) { 390 } 391 } 392 393 return new QueuedJobCount(qlen); 394 } 395 396 private QueuedJobCount getQueuedJobCountAIX() { 397 // On AIX there should not be a blank after '-a'. 398 String command = "/usr/bin/lpstat -a" + printer; 399 String results[]= UnixPrintServiceLookup.execCmd(command); 400 401 // Remove headers and bogus entries added by remote printers. 402 results = filterPrinterNamesAIX(results); 403 404 int qlen = 0; 405 if (results != null && results.length > 0){ 406 for (int i = 0; i < results.length; i++) { 407 if (results[i].contains("QUEUED")){ 408 qlen ++; 409 } 410 } 411 } 412 return new QueuedJobCount(qlen); 413 } 414 415 private QueuedJobCount getQueuedJobCount() { 416 if (UnixPrintServiceLookup.isSysV()) { 417 return getQueuedJobCountSysV(); 418 } else if (UnixPrintServiceLookup.isBSD()) { 419 return getQueuedJobCountBSD(); 420 } else if (UnixPrintServiceLookup.isAIX()) { 421 return getQueuedJobCountAIX(); 422 } else { 423 return new QueuedJobCount(0); 424 } 425 } 426 427 private PrintServiceAttributeSet getSysVServiceAttributes() { 428 PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet(); 429 attrs.add(getQueuedJobCountSysV()); 430 attrs.add(getPrinterIsAcceptingJobsSysV()); 431 return attrs; 432 } 433 434 private PrintServiceAttributeSet getBSDServiceAttributes() { 435 PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet(); 436 attrs.add(getQueuedJobCountBSD()); 437 attrs.add(getPrinterIsAcceptingJobsBSD()); 438 return attrs; 439 } 440 441 private PrintServiceAttributeSet getAIXServiceAttributes() { 442 PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet(); 443 attrs.add(getQueuedJobCountAIX()); 444 attrs.add(getPrinterIsAcceptingJobsAIX()); 445 return attrs; 446 } 447 448 private boolean isSupportedCopies(Copies copies) { 449 int numCopies = copies.getValue(); 450 return (numCopies > 0 && numCopies < MAXCOPIES); 451 } 452 453 private boolean isSupportedMedia(MediaSizeName msn) { 454 for (int i=0; i<mediaSizes.length; i++) { 455 if (msn.equals(mediaSizes[i])) { 456 return true; 457 } 458 } 459 return false; 460 } 461 462 public DocPrintJob createPrintJob() { 463 SecurityManager security = System.getSecurityManager(); 464 if (security != null) { 465 security.checkPrintJobAccess(); 466 } 467 return new UnixPrintJob(this); 468 } 469 470 private PrintServiceAttributeSet getDynamicAttributes() { 471 if (UnixPrintServiceLookup.isSysV()) { 472 return getSysVServiceAttributes(); 473 } else if (UnixPrintServiceLookup.isAIX()) { 474 return getAIXServiceAttributes(); 475 } else { 476 return getBSDServiceAttributes(); 477 } 478 } 479 480 public PrintServiceAttributeSet getUpdatedAttributes() { 481 PrintServiceAttributeSet currSet = getDynamicAttributes(); 482 if (lastSet == null) { 483 lastSet = currSet; 484 return AttributeSetUtilities.unmodifiableView(currSet); 485 } else { 486 PrintServiceAttributeSet updates = 487 new HashPrintServiceAttributeSet(); 488 Attribute []attrs = currSet.toArray(); 489 Attribute attr; 490 for (int i=0; i<attrs.length; i++) { 491 attr = attrs[i]; 492 if (!lastSet.containsValue(attr)) { 493 updates.add(attr); 494 } 495 } 496 lastSet = currSet; 497 return AttributeSetUtilities.unmodifiableView(updates); 498 } 499 } 500 501 public void wakeNotifier() { 502 synchronized (this) { 503 if (notifier != null) { 504 notifier.wake(); 505 } 506 } 507 } 508 509 public void addPrintServiceAttributeListener( 510 PrintServiceAttributeListener listener) { 511 synchronized (this) { 512 if (listener == null) { 513 return; 514 } 515 if (notifier == null) { 516 notifier = new ServiceNotifier(this); 517 } 518 notifier.addListener(listener); 519 } 520 } 521 522 public void removePrintServiceAttributeListener( 523 PrintServiceAttributeListener listener) { 524 synchronized (this) { 525 if (listener == null || notifier == null ) { 526 return; 527 } 528 notifier.removeListener(listener); 529 if (notifier.isEmpty()) { 530 notifier.stopNotifier(); 531 notifier = null; 532 } 533 } 534 } 535 536 @SuppressWarnings("unchecked") 537 public <T extends PrintServiceAttribute> 538 T getAttribute(Class<T> category) 539 { 540 if (category == null) { 541 throw new NullPointerException("category"); 542 } 543 if (!(PrintServiceAttribute.class.isAssignableFrom(category))) { 544 throw new IllegalArgumentException("Not a PrintServiceAttribute"); 545 } 546 547 if (category == PrinterName.class) { 548 return (T)getPrinterName(); 549 } else if (category == PrinterState.class) { 550 return (T)getPrinterState(); 551 } else if (category == PrinterStateReasons.class) { 552 return (T)getPrinterStateReasons(); 553 } else if (category == QueuedJobCount.class) { 554 return (T)getQueuedJobCount(); 555 } else if (category == PrinterIsAcceptingJobs.class) { 556 return (T)getPrinterIsAcceptingJobs(); 557 } else { 558 return null; 559 } 560 } 561 562 public PrintServiceAttributeSet getAttributes() { 563 PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet(); 564 attrs.add(getPrinterName()); 565 attrs.add(getPrinterIsAcceptingJobs()); 566 PrinterState prnState = getPrinterState(); 567 if (prnState != null) { 568 attrs.add(prnState); 569 } 570 PrinterStateReasons prnStateReasons = getPrinterStateReasons(); 571 if (prnStateReasons != null) { 572 attrs.add(prnStateReasons); 573 } 574 attrs.add(getQueuedJobCount()); 575 return AttributeSetUtilities.unmodifiableView(attrs); 576 } 577 578 private void initSupportedDocFlavors() { 579 String hostEnc = DocFlavor.hostEncoding.toLowerCase(Locale.ENGLISH); 580 if (!hostEnc.equals("utf-8") && !hostEnc.equals("utf-16") && 581 !hostEnc.equals("utf-16be") && !hostEnc.equals("utf-16le") && 582 !hostEnc.equals("us-ascii")) { 583 584 int len = supportedDocFlavorsInit.length; 585 DocFlavor[] flavors = 586 new DocFlavor[len + supportedHostDocFlavors.length]; 587 // copy host encoding flavors 588 System.arraycopy(supportedHostDocFlavors, 0, flavors, 589 len, supportedHostDocFlavors.length); 590 System.arraycopy(supportedDocFlavorsInit, 0, flavors, 0, len); 591 592 supportedDocFlavors = flavors; 593 } else { 594 supportedDocFlavors = supportedDocFlavorsInit; 595 } 596 } 597 598 public DocFlavor[] getSupportedDocFlavors() { 599 if (supportedDocFlavors == null) { 600 initSupportedDocFlavors(); 601 } 602 int len = supportedDocFlavors.length; 603 DocFlavor[] flavors = new DocFlavor[len]; 604 System.arraycopy(supportedDocFlavors, 0, flavors, 0, len); 605 606 return flavors; 607 } 608 609 public boolean isDocFlavorSupported(DocFlavor flavor) { 610 if (supportedDocFlavors == null) { 611 initSupportedDocFlavors(); 612 } 613 for (int f=0; f<supportedDocFlavors.length; f++) { 614 if (flavor.equals(supportedDocFlavors[f])) { 615 return true; 616 } 617 } 618 return false; 619 } 620 621 public Class<?>[] getSupportedAttributeCategories() { 622 int totalCats = otherAttrCats.length; 623 Class<?>[] cats = new Class<?>[totalCats]; 624 System.arraycopy(otherAttrCats, 0, cats, 0, otherAttrCats.length); 625 return cats; 626 } 627 628 public boolean 629 isAttributeCategorySupported(Class<? extends Attribute> category) 630 { 631 if (category == null) { 632 throw new NullPointerException("null category"); 633 } 634 if (!(Attribute.class.isAssignableFrom(category))) { 635 throw new IllegalArgumentException(category + 636 " is not an Attribute"); 637 } 638 639 for (int i=0;i<otherAttrCats.length;i++) { 640 if (category == otherAttrCats[i]) { 641 return true; 642 } 643 } 644 return false; 645 } 646 647 /* return defaults for all attributes for which there is a default 648 * value 649 */ 650 public Object 651 getDefaultAttributeValue(Class<? extends Attribute> category) 652 { 653 if (category == null) { 654 throw new NullPointerException("null category"); 655 } 656 if (!Attribute.class.isAssignableFrom(category)) { 657 throw new IllegalArgumentException(category + 658 " is not an Attribute"); 659 } 660 661 if (!isAttributeCategorySupported(category)) { 662 return null; 663 } 664 665 if (category == Copies.class) { 666 return new Copies(1); 667 } else if (category == Chromaticity.class) { 668 return Chromaticity.COLOR; 669 } else if (category == Destination.class) { 670 try { 671 return new Destination((new File("out.ps")).toURI()); 672 } catch (SecurityException se) { 673 try { 674 return new Destination(new URI("file:out.ps")); 675 } catch (URISyntaxException e) { 676 return null; 677 } 678 } 679 } else if (category == Fidelity.class) { 680 return Fidelity.FIDELITY_FALSE; 681 } else if (category == JobName.class) { 682 return new JobName("Java Printing", null); 683 } else if (category == JobSheets.class) { 684 return JobSheets.STANDARD; 685 } else if (category == Media.class) { 686 String defaultCountry = Locale.getDefault().getCountry(); 687 if (defaultCountry != null && 688 (defaultCountry.equals("") || 689 defaultCountry.equals(Locale.US.getCountry()) || 690 defaultCountry.equals(Locale.CANADA.getCountry()))) { 691 return MediaSizeName.NA_LETTER; 692 } else { 693 return MediaSizeName.ISO_A4; 694 } 695 } else if (category == MediaPrintableArea.class) { 696 String defaultCountry = Locale.getDefault().getCountry(); 697 float iw, ih; 698 if (defaultCountry != null && 699 (defaultCountry.equals("") || 700 defaultCountry.equals(Locale.US.getCountry()) || 701 defaultCountry.equals(Locale.CANADA.getCountry()))) { 702 iw = MediaSize.NA.LETTER.getX(Size2DSyntax.INCH) - 0.5f; 703 ih = MediaSize.NA.LETTER.getY(Size2DSyntax.INCH) - 0.5f; 704 } else { 705 iw = MediaSize.ISO.A4.getX(Size2DSyntax.INCH) - 0.5f; 706 ih = MediaSize.ISO.A4.getY(Size2DSyntax.INCH) - 0.5f; 707 } 708 return new MediaPrintableArea(0.25f, 0.25f, iw, ih, 709 MediaPrintableArea.INCH); 710 } else if (category == OrientationRequested.class) { 711 return OrientationRequested.PORTRAIT; 712 } else if (category == PageRanges.class) { 713 return new PageRanges(1, Integer.MAX_VALUE); 714 } else if (category == RequestingUserName.class) { 715 String userName = ""; 716 try { 717 userName = System.getProperty("user.name", ""); 718 } catch (SecurityException se) { 719 } 720 return new RequestingUserName(userName, null); 721 } else if (category == SheetCollate.class) { 722 return SheetCollate.UNCOLLATED; 723 } else if (category == Sides.class) { 724 return Sides.ONE_SIDED; 725 } else 726 return null; 727 } 728 729 730 private boolean isAutoSense(DocFlavor flavor) { 731 if (flavor.equals(DocFlavor.BYTE_ARRAY.AUTOSENSE) || 732 flavor.equals(DocFlavor.INPUT_STREAM.AUTOSENSE) || 733 flavor.equals(DocFlavor.URL.AUTOSENSE)) { 734 return true; 735 } 736 else { 737 return false; 738 } 739 } 740 741 public Object 742 getSupportedAttributeValues(Class<? extends Attribute> category, 743 DocFlavor flavor, 744 AttributeSet attributes) 745 { 746 747 if (category == null) { 748 throw new NullPointerException("null category"); 749 } 750 if (!Attribute.class.isAssignableFrom(category)) { 751 throw new IllegalArgumentException(category + 752 " does not implement Attribute"); 753 } 754 if (flavor != null) { 755 if (!isDocFlavorSupported(flavor)) { 756 throw new IllegalArgumentException(flavor + 757 " is an unsupported flavor"); 758 } else if (isAutoSense(flavor)) { 759 return null; 760 } 761 } 762 763 if (!isAttributeCategorySupported(category)) { 764 return null; 765 } 766 767 if (category == Chromaticity.class) { 768 if (flavor == null || isServiceFormattedFlavor(flavor)) { 769 Chromaticity[]arr = new Chromaticity[1]; 770 arr[0] = Chromaticity.COLOR; 771 return (arr); 772 } else { 773 return null; 774 } 775 } else if (category == Destination.class) { 776 try { 777 return new Destination((new File("out.ps")).toURI()); 778 } catch (SecurityException se) { 779 try { 780 return new Destination(new URI("file:out.ps")); 781 } catch (URISyntaxException e) { 782 return null; 783 } 784 } 785 } else if (category == JobName.class) { 786 return new JobName("Java Printing", null); 787 } else if (category == JobSheets.class) { 788 JobSheets arr[] = new JobSheets[2]; 789 arr[0] = JobSheets.NONE; 790 arr[1] = JobSheets.STANDARD; 791 return arr; 792 } else if (category == RequestingUserName.class) { 793 String userName = ""; 794 try { 795 userName = System.getProperty("user.name", ""); 796 } catch (SecurityException se) { 797 } 798 return new RequestingUserName(userName, null); 799 } else if (category == OrientationRequested.class) { 800 if (flavor == null || isServiceFormattedFlavor(flavor)) { 801 OrientationRequested []arr = new OrientationRequested[3]; 802 arr[0] = OrientationRequested.PORTRAIT; 803 arr[1] = OrientationRequested.LANDSCAPE; 804 arr[2] = OrientationRequested.REVERSE_LANDSCAPE; 805 return arr; 806 } else { 807 return null; 808 } 809 } else if ((category == Copies.class) || 810 (category == CopiesSupported.class)) { 811 if (flavor == null || 812 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) || 813 flavor.equals(DocFlavor.URL.POSTSCRIPT) || 814 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) { 815 return new CopiesSupported(1, MAXCOPIES); 816 } else { 817 return null; 818 } 819 } else if (category == Media.class) { 820 Media []arr = new Media[mediaSizes.length]; 821 System.arraycopy(mediaSizes, 0, arr, 0, mediaSizes.length); 822 return arr; 823 } else if (category == Fidelity.class) { 824 Fidelity []arr = new Fidelity[2]; 825 arr[0] = Fidelity.FIDELITY_FALSE; 826 arr[1] = Fidelity.FIDELITY_TRUE; 827 return arr; 828 } else if (category == MediaPrintableArea.class) { 829 /* The code below implements the behaviour that if no Media or 830 * MediaSize attribute is specified, return an array of 831 * MediaPrintableArea, one for each supported Media. 832 * If a MediaSize is specified, return a MPA consistent for that, 833 * and if a Media is specified locate its MediaSize and return 834 * its MPA, and if none is found, return an MPA for the default 835 * Media for this service. 836 */ 837 if (attributes == null) { 838 return getAllPrintableAreas(); 839 } 840 MediaSize mediaSize = (MediaSize)attributes.get(MediaSize.class); 841 Media media = (Media)attributes.get(Media.class); 842 MediaPrintableArea []arr = new MediaPrintableArea[1]; 843 if (mediaSize == null) { 844 if (media instanceof MediaSizeName) { 845 MediaSizeName msn = (MediaSizeName)media; 846 mediaSize = MediaSize.getMediaSizeForName(msn); 847 if (mediaSize == null) { 848 /* try to get a size from the default media */ 849 media = (Media)getDefaultAttributeValue(Media.class); 850 if (media instanceof MediaSizeName) { 851 msn = (MediaSizeName)media; 852 mediaSize = MediaSize.getMediaSizeForName(msn); 853 } 854 if (mediaSize == null) { 855 /* shouldn't happen, return a default */ 856 arr[0] = new MediaPrintableArea(0.25f, 0.25f, 857 8f, 10.5f, 858 MediaSize.INCH); 859 return arr; 860 } 861 } 862 } else { 863 return getAllPrintableAreas(); 864 } 865 } 866 /* If reach here MediaSize is non-null */ 867 assert mediaSize != null; 868 arr[0] = new MediaPrintableArea(0.25f, 0.25f, 869 mediaSize.getX(MediaSize.INCH)-0.5f, 870 mediaSize.getY(MediaSize.INCH)-0.5f, 871 MediaSize.INCH); 872 return arr; 873 } else if (category == PageRanges.class) { 874 if (flavor == null || 875 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 876 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 877 PageRanges []arr = new PageRanges[1]; 878 arr[0] = new PageRanges(1, Integer.MAX_VALUE); 879 return arr; 880 } else { 881 return null; 882 } 883 } else if (category == SheetCollate.class) { 884 if (flavor == null || 885 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 886 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 887 SheetCollate []arr = new SheetCollate[2]; 888 arr[0] = SheetCollate.UNCOLLATED; 889 arr[1] = SheetCollate.COLLATED; 890 return arr; 891 } else { 892 return null; 893 } 894 } else if (category == Sides.class) { 895 if (flavor == null || 896 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 897 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { 898 Sides []arr = new Sides[3]; 899 arr[0] = Sides.ONE_SIDED; 900 arr[1] = Sides.TWO_SIDED_LONG_EDGE; 901 arr[2] = Sides.TWO_SIDED_SHORT_EDGE; 902 return arr; 903 } else { 904 return null; 905 } 906 } else { 907 return null; 908 } 909 } 910 911 private static MediaPrintableArea[] mpas = null; 912 private MediaPrintableArea[] getAllPrintableAreas() { 913 914 if (mpas == null) { 915 Media[] media = (Media[])getSupportedAttributeValues(Media.class, 916 null, null); 917 mpas = new MediaPrintableArea[media.length]; 918 for (int i=0; i< mpas.length; i++) { 919 if (media[i] instanceof MediaSizeName) { 920 MediaSizeName msn = (MediaSizeName)media[i]; 921 MediaSize mediaSize = MediaSize.getMediaSizeForName(msn); 922 if (mediaSize == null) { 923 mpas[i] = (MediaPrintableArea) 924 getDefaultAttributeValue(MediaPrintableArea.class); 925 } else { 926 mpas[i] = new MediaPrintableArea(0.25f, 0.25f, 927 mediaSize.getX(MediaSize.INCH)-0.5f, 928 mediaSize.getY(MediaSize.INCH)-0.5f, 929 MediaSize.INCH); 930 } 931 } 932 } 933 } 934 MediaPrintableArea[] mpasCopy = new MediaPrintableArea[mpas.length]; 935 System.arraycopy(mpas, 0, mpasCopy, 0, mpas.length); 936 return mpasCopy; 937 } 938 939 /* Is this one of the flavors that this service explicitly 940 * generates postscript for, and so can control how it is rendered? 941 */ 942 private boolean isServiceFormattedFlavor(DocFlavor flavor) { 943 return 944 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 945 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) || 946 flavor.equals(DocFlavor.BYTE_ARRAY.GIF) || 947 flavor.equals(DocFlavor.INPUT_STREAM.GIF) || 948 flavor.equals(DocFlavor.URL.GIF) || 949 flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) || 950 flavor.equals(DocFlavor.INPUT_STREAM.JPEG) || 951 flavor.equals(DocFlavor.URL.JPEG) || 952 flavor.equals(DocFlavor.BYTE_ARRAY.PNG) || 953 flavor.equals(DocFlavor.INPUT_STREAM.PNG) || 954 flavor.equals(DocFlavor.URL.PNG); 955 } 956 957 public boolean isAttributeValueSupported(Attribute attr, 958 DocFlavor flavor, 959 AttributeSet attributes) { 960 if (attr == null) { 961 throw new NullPointerException("null attribute"); 962 } 963 if (flavor != null) { 964 if (!isDocFlavorSupported(flavor)) { 965 throw new IllegalArgumentException(flavor + 966 " is an unsupported flavor"); 967 } else if (isAutoSense(flavor)) { 968 return false; 969 } 970 } 971 Class<? extends Attribute> category = attr.getCategory(); 972 if (!isAttributeCategorySupported(category)) { 973 return false; 974 } 975 else if (attr.getCategory() == Chromaticity.class) { 976 if (flavor == null || isServiceFormattedFlavor(flavor)) { 977 return attr == Chromaticity.COLOR; 978 } else { 979 return false; 980 } 981 } 982 else if (attr.getCategory() == Copies.class) { 983 return (flavor == null || 984 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) || 985 flavor.equals(DocFlavor.URL.POSTSCRIPT) || 986 flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) && 987 isSupportedCopies((Copies)attr); 988 } else if (attr.getCategory() == Destination.class) { 989 URI uri = ((Destination)attr).getURI(); 990 if ("file".equals(uri.getScheme()) && 991 !(uri.getSchemeSpecificPart().equals(""))) { 992 return true; 993 } else { 994 return false; 995 } 996 } else if (attr.getCategory() == Media.class) { 997 if (attr instanceof MediaSizeName) { 998 return isSupportedMedia((MediaSizeName)attr); 999 } else { 1000 return false; 1001 } 1002 } else if (attr.getCategory() == OrientationRequested.class) { 1003 if (attr == OrientationRequested.REVERSE_PORTRAIT || 1004 (flavor != null) && 1005 !isServiceFormattedFlavor(flavor)) { 1006 return false; 1007 } 1008 } else if (attr.getCategory() == PageRanges.class) { 1009 if (flavor != null && 1010 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1011 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 1012 return false; 1013 } 1014 } else if (attr.getCategory() == SheetCollate.class) { 1015 if (flavor != null && 1016 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1017 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 1018 return false; 1019 } 1020 } else if (attr.getCategory() == Sides.class) { 1021 if (flavor != null && 1022 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || 1023 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) { 1024 return false; 1025 } 1026 } 1027 return true; 1028 } 1029 1030 public AttributeSet getUnsupportedAttributes(DocFlavor flavor, 1031 AttributeSet attributes) { 1032 1033 if (flavor != null && !isDocFlavorSupported(flavor)) { 1034 throw new IllegalArgumentException("flavor " + flavor + 1035 "is not supported"); 1036 } 1037 1038 if (attributes == null) { 1039 return null; 1040 } 1041 1042 Attribute attr; 1043 AttributeSet unsupp = new HashAttributeSet(); 1044 Attribute []attrs = attributes.toArray(); 1045 for (int i=0; i<attrs.length; i++) { 1046 try { 1047 attr = attrs[i]; 1048 if (!isAttributeCategorySupported(attr.getCategory())) { 1049 unsupp.add(attr); 1050 } else if (!isAttributeValueSupported(attr, flavor, 1051 attributes)) { 1052 unsupp.add(attr); 1053 } 1054 } catch (ClassCastException e) { 1055 } 1056 } 1057 if (unsupp.isEmpty()) { 1058 return null; 1059 } else { 1060 return unsupp; 1061 } 1062 } 1063 1064 public ServiceUIFactory getServiceUIFactory() { 1065 return null; 1066 } 1067 1068 public String toString() { 1069 return "Unix Printer : " + getName(); 1070 } 1071 1072 public boolean equals(Object obj) { 1073 return (obj == this || 1074 (obj instanceof UnixPrintService && 1075 ((UnixPrintService)obj).getName().equals(getName()))); 1076 } 1077 1078 public int hashCode() { 1079 return this.getClass().hashCode()+getName().hashCode(); 1080 } 1081 1082 public boolean usesClass(Class<?> c) { 1083 return (c == sun.print.PSPrinterJob.class); 1084 } 1085 1086 }