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 
UnixPrintService(String name)198     UnixPrintService(String name) {
199         if (name == null) {
200             throw new IllegalArgumentException("null printer name");
201         }
202         printer = name;
203         isInvalid = false;
204     }
205 
invalidateService()206     public void invalidateService() {
207         isInvalid = true;
208     }
209 
getName()210     public String getName() {
211         return printer;
212     }
213 
getPrinterName()214     private PrinterName getPrinterName() {
215         if (name == null) {
216             name = new PrinterName(printer, null);
217         }
218         return name;
219     }
220 
getPrinterIsAcceptingJobsSysV()221     private PrinterIsAcceptingJobs getPrinterIsAcceptingJobsSysV() {
222         String command = "/usr/bin/lpstat -a " + printer;
223         String results[]= PrintServiceLookupProvider.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 
getPrinterIsAcceptingJobsBSD()246     private PrinterIsAcceptingJobs getPrinterIsAcceptingJobsBSD() {
247         if (PrintServiceLookupProvider.cmdIndex ==
248             PrintServiceLookupProvider.UNINITIALIZED) {
249 
250             PrintServiceLookupProvider.cmdIndex =
251                 PrintServiceLookupProvider.getBSDCommandIndex();
252         }
253 
254         String command = "/usr/sbin/lpc status " + printer
255             + lpcStatusCom[PrintServiceLookupProvider.cmdIndex];
256         String results[]= PrintServiceLookupProvider.execCmd(command);
257 
258         if (results != null && results.length > 0) {
259             if (PrintServiceLookupProvider.cmdIndex ==
260                 PrintServiceLookupProvider.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 PrintServiceLookupProvider.
filterPrinterNamesAIX(String[] posPrinters)280     protected static String[] filterPrinterNamesAIX(String[] posPrinters) {
281         ArrayList 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 (String[])printers.toArray(new String[printers.size()]);
299     }
300 
getPrinterIsAcceptingJobsAIX()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[]= PrintServiceLookupProvider.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 
getPrinterIsAcceptingJobs()322     private PrinterIsAcceptingJobs getPrinterIsAcceptingJobs() {
323         if (PrintServiceLookupProvider.isSysV()) {
324             return getPrinterIsAcceptingJobsSysV();
325         } else if (PrintServiceLookupProvider.isBSD()) {
326             return getPrinterIsAcceptingJobsBSD();
327         } else if (PrintServiceLookupProvider.isAIX()) {
328             return getPrinterIsAcceptingJobsAIX();
329         } else {
330             return PrinterIsAcceptingJobs.ACCEPTING_JOBS;
331         }
332     }
333 
getPrinterState()334     private PrinterState getPrinterState() {
335         if (isInvalid) {
336             return PrinterState.STOPPED;
337         } else {
338             return null;
339         }
340     }
341 
getPrinterStateReasons()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 
getQueuedJobCountSysV()352     private QueuedJobCount getQueuedJobCountSysV() {
353         String command = "/usr/bin/lpstat -R " + printer;
354         String results[]= PrintServiceLookupProvider.execCmd(command);
355         int qlen = (results == null) ? 0 : results.length;
356 
357         return new QueuedJobCount(qlen);
358     }
359 
getQueuedJobCountBSD()360     private QueuedJobCount getQueuedJobCountBSD() {
361         if (PrintServiceLookupProvider.cmdIndex ==
362             PrintServiceLookupProvider.UNINITIALIZED) {
363 
364             PrintServiceLookupProvider.cmdIndex =
365                 PrintServiceLookupProvider.getBSDCommandIndex();
366         }
367 
368         int qlen = 0;
369         String command = "/usr/sbin/lpc status " + printer
370             + lpcQueueCom[PrintServiceLookupProvider.cmdIndex];
371         String results[] = PrintServiceLookupProvider.execCmd(command);
372 
373         if (results != null && results.length > 0) {
374             String queued;
375             if (PrintServiceLookupProvider.cmdIndex ==
376                 PrintServiceLookupProvider.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 
getQueuedJobCountAIX()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[]=  PrintServiceLookupProvider.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 
getQueuedJobCount()415     private QueuedJobCount getQueuedJobCount() {
416         if (PrintServiceLookupProvider.isSysV()) {
417             return getQueuedJobCountSysV();
418         } else if (PrintServiceLookupProvider.isBSD()) {
419             return getQueuedJobCountBSD();
420         } else if (PrintServiceLookupProvider.isAIX()) {
421             return getQueuedJobCountAIX();
422         } else {
423             return new QueuedJobCount(0);
424         }
425     }
426 
getSysVServiceAttributes()427     private PrintServiceAttributeSet getSysVServiceAttributes() {
428         PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet();
429         attrs.add(getQueuedJobCountSysV());
430         attrs.add(getPrinterIsAcceptingJobsSysV());
431         return attrs;
432     }
433 
getBSDServiceAttributes()434     private PrintServiceAttributeSet getBSDServiceAttributes() {
435         PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet();
436         attrs.add(getQueuedJobCountBSD());
437         attrs.add(getPrinterIsAcceptingJobsBSD());
438         return attrs;
439     }
440 
getAIXServiceAttributes()441     private PrintServiceAttributeSet getAIXServiceAttributes() {
442         PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet();
443         attrs.add(getQueuedJobCountAIX());
444         attrs.add(getPrinterIsAcceptingJobsAIX());
445         return attrs;
446     }
447 
isSupportedCopies(Copies copies)448     private boolean isSupportedCopies(Copies copies) {
449         int numCopies = copies.getValue();
450         return (numCopies > 0 && numCopies < MAXCOPIES);
451     }
452 
isSupportedMedia(MediaSizeName msn)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 
createPrintJob()462     public DocPrintJob createPrintJob() {
463       SecurityManager security = System.getSecurityManager();
464       if (security != null) {
465         security.checkPrintJobAccess();
466       }
467         return new UnixPrintJob(this);
468     }
469 
getDynamicAttributes()470     private PrintServiceAttributeSet getDynamicAttributes() {
471         if (PrintServiceLookupProvider.isSysV()) {
472             return getSysVServiceAttributes();
473         } else if (PrintServiceLookupProvider.isAIX()) {
474             return getAIXServiceAttributes();
475         } else {
476             return getBSDServiceAttributes();
477         }
478     }
479 
getUpdatedAttributes()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 
wakeNotifier()501     public void wakeNotifier() {
502         synchronized (this) {
503             if (notifier != null) {
504                 notifier.wake();
505             }
506         }
507     }
508 
addPrintServiceAttributeListener( PrintServiceAttributeListener listener)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 
removePrintServiceAttributeListener( PrintServiceAttributeListener listener)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     public <T extends PrintServiceAttribute>
getAttribute(Class<T> category)537         T getAttribute(Class<T> category)
538     {
539         if (category == null) {
540             throw new NullPointerException("category");
541         }
542         if (!(PrintServiceAttribute.class.isAssignableFrom(category))) {
543             throw new IllegalArgumentException("Not a PrintServiceAttribute");
544         }
545 
546         if (category == PrinterName.class) {
547             return (T)getPrinterName();
548         } else if (category == PrinterState.class) {
549             return (T)getPrinterState();
550         } else if (category == PrinterStateReasons.class) {
551             return (T)getPrinterStateReasons();
552         } else if (category == QueuedJobCount.class) {
553             return (T)getQueuedJobCount();
554         } else if (category == PrinterIsAcceptingJobs.class) {
555             return (T)getPrinterIsAcceptingJobs();
556         } else {
557             return null;
558         }
559     }
560 
getAttributes()561     public PrintServiceAttributeSet getAttributes() {
562         PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet();
563         attrs.add(getPrinterName());
564         attrs.add(getPrinterIsAcceptingJobs());
565         PrinterState prnState = getPrinterState();
566         if (prnState != null) {
567             attrs.add(prnState);
568         }
569         PrinterStateReasons prnStateReasons = getPrinterStateReasons();
570         if (prnStateReasons != null) {
571             attrs.add(prnStateReasons);
572         }
573         attrs.add(getQueuedJobCount());
574         return AttributeSetUtilities.unmodifiableView(attrs);
575     }
576 
initSupportedDocFlavors()577     private void initSupportedDocFlavors() {
578         String hostEnc = DocFlavor.hostEncoding.toLowerCase(Locale.ENGLISH);
579         if (!hostEnc.equals("utf-8") && !hostEnc.equals("utf-16") &&
580             !hostEnc.equals("utf-16be") && !hostEnc.equals("utf-16le") &&
581             !hostEnc.equals("us-ascii")) {
582 
583             int len = supportedDocFlavorsInit.length;
584             DocFlavor[] flavors =
585                 new DocFlavor[len + supportedHostDocFlavors.length];
586             // copy host encoding flavors
587             System.arraycopy(supportedHostDocFlavors, 0, flavors,
588                              len, supportedHostDocFlavors.length);
589             System.arraycopy(supportedDocFlavorsInit, 0, flavors, 0, len);
590 
591             supportedDocFlavors = flavors;
592         } else {
593             supportedDocFlavors = supportedDocFlavorsInit;
594         }
595     }
596 
getSupportedDocFlavors()597     public DocFlavor[] getSupportedDocFlavors() {
598         if (supportedDocFlavors == null) {
599             initSupportedDocFlavors();
600         }
601         int len = supportedDocFlavors.length;
602         DocFlavor[] flavors = new DocFlavor[len];
603         System.arraycopy(supportedDocFlavors, 0, flavors, 0, len);
604 
605         return flavors;
606     }
607 
isDocFlavorSupported(DocFlavor flavor)608     public boolean isDocFlavorSupported(DocFlavor flavor) {
609         if (supportedDocFlavors == null) {
610             initSupportedDocFlavors();
611         }
612         for (int f=0; f<supportedDocFlavors.length; f++) {
613             if (flavor.equals(supportedDocFlavors[f])) {
614                 return true;
615             }
616         }
617         return false;
618     }
619 
getSupportedAttributeCategories()620     public Class[] getSupportedAttributeCategories() {
621         int totalCats = otherAttrCats.length;
622         Class [] cats = new Class[totalCats];
623         System.arraycopy(otherAttrCats, 0, cats, 0, otherAttrCats.length);
624         return cats;
625     }
626 
627     public boolean
isAttributeCategorySupported(Class<? extends Attribute> category)628         isAttributeCategorySupported(Class<? extends Attribute> category)
629     {
630         if (category == null) {
631             throw new NullPointerException("null category");
632         }
633         if (!(Attribute.class.isAssignableFrom(category))) {
634             throw new IllegalArgumentException(category +
635                                              " is not an Attribute");
636         }
637 
638         for (int i=0;i<otherAttrCats.length;i++) {
639             if (category == otherAttrCats[i]) {
640                 return true;
641             }
642         }
643         return false;
644     }
645 
646     /* return defaults for all attributes for which there is a default
647      * value
648      */
649     public Object
getDefaultAttributeValue(Class<? extends Attribute> category)650         getDefaultAttributeValue(Class<? extends Attribute> category)
651     {
652         if (category == null) {
653             throw new NullPointerException("null category");
654         }
655         if (!Attribute.class.isAssignableFrom(category)) {
656             throw new IllegalArgumentException(category +
657                                              " is not an Attribute");
658         }
659 
660         if (!isAttributeCategorySupported(category)) {
661             return null;
662         }
663 
664         if (category == Copies.class) {
665             return new Copies(1);
666         } else if (category == Chromaticity.class) {
667             return Chromaticity.COLOR;
668         } else if (category == Destination.class) {
669             try {
670                 return new Destination((new File("out.ps")).toURI());
671             } catch (SecurityException se) {
672                 try {
673                     return new Destination(new URI("file:out.ps"));
674                 } catch (URISyntaxException e) {
675                     return null;
676                 }
677             }
678         } else if (category == Fidelity.class) {
679             return Fidelity.FIDELITY_FALSE;
680         } else if (category == JobName.class) {
681             return new JobName("Java Printing", null);
682         } else if (category == JobSheets.class) {
683             return JobSheets.STANDARD;
684         } else if (category == Media.class) {
685             String defaultCountry = Locale.getDefault().getCountry();
686             if (defaultCountry != null &&
687                 (defaultCountry.equals("") ||
688                  defaultCountry.equals(Locale.US.getCountry()) ||
689                  defaultCountry.equals(Locale.CANADA.getCountry()))) {
690                 return MediaSizeName.NA_LETTER;
691             } else {
692                  return MediaSizeName.ISO_A4;
693             }
694         } else if (category == MediaPrintableArea.class) {
695             String defaultCountry = Locale.getDefault().getCountry();
696             float iw, ih;
697             if (defaultCountry != null &&
698                 (defaultCountry.equals("") ||
699                  defaultCountry.equals(Locale.US.getCountry()) ||
700                  defaultCountry.equals(Locale.CANADA.getCountry()))) {
701                 iw = MediaSize.NA.LETTER.getX(Size2DSyntax.INCH) - 0.5f;
702                 ih = MediaSize.NA.LETTER.getY(Size2DSyntax.INCH) - 0.5f;
703             } else {
704                 iw = MediaSize.ISO.A4.getX(Size2DSyntax.INCH) - 0.5f;
705                 ih = MediaSize.ISO.A4.getY(Size2DSyntax.INCH) - 0.5f;
706             }
707             return new MediaPrintableArea(0.25f, 0.25f, iw, ih,
708                                           MediaPrintableArea.INCH);
709         } else if (category == OrientationRequested.class) {
710             return OrientationRequested.PORTRAIT;
711         } else if (category == PageRanges.class) {
712             return new PageRanges(1, Integer.MAX_VALUE);
713         } else if (category == RequestingUserName.class) {
714             String userName = "";
715             try {
716               userName = System.getProperty("user.name", "");
717             } catch (SecurityException se) {
718             }
719             return new RequestingUserName(userName, null);
720         } else if (category == SheetCollate.class) {
721             return SheetCollate.UNCOLLATED;
722         } else if (category == Sides.class) {
723             return Sides.ONE_SIDED;
724         } else
725             return null;
726     }
727 
728 
isAutoSense(DocFlavor flavor)729     private boolean isAutoSense(DocFlavor flavor) {
730         if (flavor.equals(DocFlavor.BYTE_ARRAY.AUTOSENSE) ||
731             flavor.equals(DocFlavor.INPUT_STREAM.AUTOSENSE) ||
732             flavor.equals(DocFlavor.URL.AUTOSENSE)) {
733             return true;
734         }
735         else {
736             return false;
737         }
738     }
739 
740     public Object
getSupportedAttributeValues(Class<? extends Attribute> category, DocFlavor flavor, AttributeSet attributes)741         getSupportedAttributeValues(Class<? extends Attribute> category,
742                                     DocFlavor flavor,
743                                     AttributeSet attributes)
744     {
745 
746         if (category == null) {
747             throw new NullPointerException("null category");
748         }
749         if (!Attribute.class.isAssignableFrom(category)) {
750             throw new IllegalArgumentException(category +
751                                              " does not implement Attribute");
752         }
753         if (flavor != null) {
754             if (!isDocFlavorSupported(flavor)) {
755                 throw new IllegalArgumentException(flavor +
756                                                " is an unsupported flavor");
757             } else if (isAutoSense(flavor)) {
758                 return null;
759             }
760         }
761 
762         if (!isAttributeCategorySupported(category)) {
763             return null;
764         }
765 
766         if (category == Chromaticity.class) {
767             if (flavor == null || isServiceFormattedFlavor(flavor)) {
768                 Chromaticity[]arr = new Chromaticity[1];
769                 arr[0] = Chromaticity.COLOR;
770                 return (arr);
771             } else {
772                 return null;
773             }
774         } else if (category == Destination.class) {
775             try {
776                 return new Destination((new File("out.ps")).toURI());
777             } catch (SecurityException se) {
778                 try {
779                     return new Destination(new URI("file:out.ps"));
780                 } catch (URISyntaxException e) {
781                     return null;
782                 }
783             }
784         } else if (category == JobName.class) {
785             return new JobName("Java Printing", null);
786         } else if (category == JobSheets.class) {
787             JobSheets arr[] = new JobSheets[2];
788             arr[0] = JobSheets.NONE;
789             arr[1] = JobSheets.STANDARD;
790             return arr;
791         } else if (category == RequestingUserName.class) {
792             String userName = "";
793             try {
794               userName = System.getProperty("user.name", "");
795             } catch (SecurityException se) {
796             }
797             return new RequestingUserName(userName, null);
798         } else if (category == OrientationRequested.class) {
799             if (flavor == null || isServiceFormattedFlavor(flavor)) {
800                 OrientationRequested []arr = new OrientationRequested[3];
801                 arr[0] = OrientationRequested.PORTRAIT;
802                 arr[1] = OrientationRequested.LANDSCAPE;
803                 arr[2] = OrientationRequested.REVERSE_LANDSCAPE;
804                 return arr;
805             } else {
806                 return null;
807             }
808         } else if ((category == Copies.class) ||
809                    (category == CopiesSupported.class)) {
810             if (flavor == null ||
811                 !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
812                   flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
813                   flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) {
814                 return new CopiesSupported(1, MAXCOPIES);
815             } else {
816                 return null;
817             }
818         } else if (category == Media.class) {
819             Media []arr = new Media[mediaSizes.length];
820             System.arraycopy(mediaSizes, 0, arr, 0, mediaSizes.length);
821             return arr;
822         } else if (category == Fidelity.class) {
823             Fidelity []arr = new Fidelity[2];
824             arr[0] = Fidelity.FIDELITY_FALSE;
825             arr[1] = Fidelity.FIDELITY_TRUE;
826             return arr;
827         } else if (category == MediaPrintableArea.class) {
828             /* The code below implements the behaviour that if no Media or
829              * MediaSize attribute is specified, return an array of
830              * MediaPrintableArea, one for each supported Media.
831              * If a MediaSize is specified, return a MPA consistent for that,
832              * and if a Media is specified locate its MediaSize and return
833              * its MPA, and if none is found, return an MPA for the default
834              * Media for this service.
835              */
836             if (attributes == null) {
837                 return getAllPrintableAreas();
838             }
839             MediaSize mediaSize = (MediaSize)attributes.get(MediaSize.class);
840             Media media = (Media)attributes.get(Media.class);
841             MediaPrintableArea []arr = new MediaPrintableArea[1];
842             if (mediaSize == null) {
843                 if (media instanceof MediaSizeName) {
844                     MediaSizeName msn = (MediaSizeName)media;
845                     mediaSize = MediaSize.getMediaSizeForName(msn);
846                     if (mediaSize == null) {
847                         /* try to get a size from the default media */
848                         media = (Media)getDefaultAttributeValue(Media.class);
849                         if (media instanceof MediaSizeName) {
850                             msn = (MediaSizeName)media;
851                             mediaSize = MediaSize.getMediaSizeForName(msn);
852                         }
853                         if (mediaSize == null) {
854                             /* shouldn't happen, return a default */
855                             arr[0] = new MediaPrintableArea(0.25f, 0.25f,
856                                                             8f, 10.5f,
857                                                             MediaSize.INCH);
858                             return arr;
859                         }
860                     }
861                 } else {
862                     return getAllPrintableAreas();
863                 }
864             }
865             /* If reach here MediaSize is non-null */
866             assert mediaSize != null;
867             arr[0] = new MediaPrintableArea(0.25f, 0.25f,
868                                 mediaSize.getX(MediaSize.INCH)-0.5f,
869                                 mediaSize.getY(MediaSize.INCH)-0.5f,
870                                 MediaSize.INCH);
871             return arr;
872         } else if (category == PageRanges.class) {
873             if (flavor == null ||
874                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
875                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
876                 PageRanges []arr = new PageRanges[1];
877                 arr[0] = new PageRanges(1, Integer.MAX_VALUE);
878                 return arr;
879             } else {
880                 return null;
881             }
882         } else if (category == SheetCollate.class) {
883             if (flavor == null ||
884                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
885                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
886                 SheetCollate []arr = new SheetCollate[2];
887                 arr[0] = SheetCollate.UNCOLLATED;
888                 arr[1] = SheetCollate.COLLATED;
889                 return arr;
890             } else {
891                 return null;
892             }
893         } else if (category == Sides.class) {
894             if (flavor == null ||
895                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
896                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
897                 Sides []arr = new Sides[3];
898                 arr[0] = Sides.ONE_SIDED;
899                 arr[1] = Sides.TWO_SIDED_LONG_EDGE;
900                 arr[2] = Sides.TWO_SIDED_SHORT_EDGE;
901                 return arr;
902             } else {
903                 return null;
904             }
905         } else {
906             return null;
907         }
908     }
909 
910     private static MediaPrintableArea[] mpas = null;
getAllPrintableAreas()911     private MediaPrintableArea[] getAllPrintableAreas() {
912 
913         if (mpas == null) {
914             Media[] media = (Media[])getSupportedAttributeValues(Media.class,
915                                                                  null, null);
916             mpas = new MediaPrintableArea[media.length];
917             for (int i=0; i< mpas.length; i++) {
918                 if (media[i] instanceof MediaSizeName) {
919                     MediaSizeName msn = (MediaSizeName)media[i];
920                     MediaSize mediaSize = MediaSize.getMediaSizeForName(msn);
921                     if (mediaSize == null) {
922                         mpas[i] = (MediaPrintableArea)
923                             getDefaultAttributeValue(MediaPrintableArea.class);
924                     } else {
925                         mpas[i] = new MediaPrintableArea(0.25f, 0.25f,
926                                         mediaSize.getX(MediaSize.INCH)-0.5f,
927                                         mediaSize.getY(MediaSize.INCH)-0.5f,
928                                         MediaSize.INCH);
929                     }
930                 }
931             }
932         }
933         MediaPrintableArea[] mpasCopy = new MediaPrintableArea[mpas.length];
934         System.arraycopy(mpas, 0, mpasCopy, 0, mpas.length);
935         return mpasCopy;
936     }
937 
938     /* Is this one of the flavors that this service explicitly
939      * generates postscript for, and so can control how it is rendered?
940      */
isServiceFormattedFlavor(DocFlavor flavor)941     private boolean isServiceFormattedFlavor(DocFlavor flavor) {
942         return
943             flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
944             flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) ||
945             flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||
946             flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||
947             flavor.equals(DocFlavor.URL.GIF) ||
948             flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||
949             flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||
950             flavor.equals(DocFlavor.URL.JPEG) ||
951             flavor.equals(DocFlavor.BYTE_ARRAY.PNG) ||
952             flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||
953             flavor.equals(DocFlavor.URL.PNG);
954     }
955 
isAttributeValueSupported(Attribute attr, DocFlavor flavor, AttributeSet attributes)956     public boolean isAttributeValueSupported(Attribute attr,
957                                              DocFlavor flavor,
958                                              AttributeSet attributes) {
959         if (attr == null) {
960             throw new NullPointerException("null attribute");
961         }
962         if (flavor != null) {
963             if (!isDocFlavorSupported(flavor)) {
964                 throw new IllegalArgumentException(flavor +
965                                                " is an unsupported flavor");
966             } else if (isAutoSense(flavor)) {
967                 return false;
968             }
969         }
970         Class category = attr.getCategory();
971         if (!isAttributeCategorySupported(category)) {
972             return false;
973         }
974         else if (attr.getCategory() == Chromaticity.class) {
975             if (flavor == null || isServiceFormattedFlavor(flavor)) {
976                 return attr == Chromaticity.COLOR;
977             } else {
978                 return false;
979             }
980         }
981         else if (attr.getCategory() == Copies.class) {
982             return (flavor == null ||
983                    !(flavor.equals(DocFlavor.INPUT_STREAM.POSTSCRIPT) ||
984                      flavor.equals(DocFlavor.URL.POSTSCRIPT) ||
985                      flavor.equals(DocFlavor.BYTE_ARRAY.POSTSCRIPT))) &&
986                 isSupportedCopies((Copies)attr);
987         } else if (attr.getCategory() == Destination.class) {
988             URI uri = ((Destination)attr).getURI();
989                 if ("file".equals(uri.getScheme()) &&
990                     !(uri.getSchemeSpecificPart().equals(""))) {
991                 return true;
992             } else {
993             return false;
994             }
995         } else if (attr.getCategory() == Media.class) {
996             if (attr instanceof MediaSizeName) {
997                 return isSupportedMedia((MediaSizeName)attr);
998             } else {
999                 return false;
1000             }
1001         } else if (attr.getCategory() == OrientationRequested.class) {
1002             if (attr == OrientationRequested.REVERSE_PORTRAIT ||
1003                 (flavor != null) &&
1004                 !isServiceFormattedFlavor(flavor)) {
1005                 return false;
1006             }
1007         } else if (attr.getCategory() == PageRanges.class) {
1008             if (flavor != null &&
1009                 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1010                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
1011                 return false;
1012             }
1013         } else if (attr.getCategory() == SheetCollate.class) {
1014             if (flavor != null &&
1015                 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1016                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
1017                 return false;
1018             }
1019         } else if (attr.getCategory() == Sides.class) {
1020             if (flavor != null &&
1021                 !(flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) ||
1022                 flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE))) {
1023                 return false;
1024             }
1025         }
1026         return true;
1027     }
1028 
getUnsupportedAttributes(DocFlavor flavor, AttributeSet attributes)1029     public AttributeSet getUnsupportedAttributes(DocFlavor flavor,
1030                                                  AttributeSet attributes) {
1031 
1032         if (flavor != null && !isDocFlavorSupported(flavor)) {
1033             throw new IllegalArgumentException("flavor " + flavor +
1034                                                "is not supported");
1035         }
1036 
1037         if (attributes == null) {
1038             return null;
1039         }
1040 
1041         Attribute attr;
1042         AttributeSet unsupp = new HashAttributeSet();
1043         Attribute []attrs = attributes.toArray();
1044         for (int i=0; i<attrs.length; i++) {
1045             try {
1046                 attr = attrs[i];
1047                 if (!isAttributeCategorySupported(attr.getCategory())) {
1048                     unsupp.add(attr);
1049                 } else if (!isAttributeValueSupported(attr, flavor,
1050                                                       attributes)) {
1051                     unsupp.add(attr);
1052                 }
1053             } catch (ClassCastException e) {
1054             }
1055         }
1056         if (unsupp.isEmpty()) {
1057             return null;
1058         } else {
1059             return unsupp;
1060         }
1061     }
1062 
getServiceUIFactory()1063     public ServiceUIFactory getServiceUIFactory() {
1064         return null;
1065     }
1066 
toString()1067     public String toString() {
1068         return "Unix Printer : " + getName();
1069     }
1070 
equals(Object obj)1071     public boolean equals(Object obj) {
1072         return  (obj == this ||
1073                  (obj instanceof UnixPrintService &&
1074                   ((UnixPrintService)obj).getName().equals(getName())));
1075     }
1076 
hashCode()1077     public int hashCode() {
1078         return this.getClass().hashCode()+getName().hashCode();
1079     }
1080 
usesClass(Class c)1081     public boolean usesClass(Class c) {
1082         return (c == sun.print.PSPrinterJob.class);
1083     }
1084 
1085 }
1086