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
26package sun.print;
27
28import java.io.File;
29import java.net.URI;
30import java.net.URISyntaxException;
31import java.util.ArrayList;
32import java.util.Locale;
33
34import javax.print.DocFlavor;
35import javax.print.DocPrintJob;
36import javax.print.PrintService;
37import javax.print.ServiceUIFactory;
38import javax.print.attribute.Attribute;
39import javax.print.attribute.AttributeSet;
40import javax.print.attribute.AttributeSetUtilities;
41import javax.print.attribute.HashAttributeSet;
42import javax.print.attribute.PrintServiceAttribute;
43import javax.print.attribute.PrintServiceAttributeSet;
44import javax.print.attribute.HashPrintServiceAttributeSet;
45import javax.print.attribute.Size2DSyntax;
46import javax.print.attribute.standard.PrinterName;
47import javax.print.attribute.standard.PrinterIsAcceptingJobs;
48import javax.print.attribute.standard.QueuedJobCount;
49import javax.print.attribute.standard.JobName;
50import javax.print.attribute.standard.JobSheets;
51import javax.print.attribute.standard.RequestingUserName;
52import javax.print.attribute.standard.Chromaticity;
53import javax.print.attribute.standard.ColorSupported;
54import javax.print.attribute.standard.Copies;
55import javax.print.attribute.standard.CopiesSupported;
56import javax.print.attribute.standard.Destination;
57import javax.print.attribute.standard.Fidelity;
58import javax.print.attribute.standard.Media;
59import javax.print.attribute.standard.MediaPrintableArea;
60import javax.print.attribute.standard.MediaSize;
61import javax.print.attribute.standard.MediaSizeName;
62import javax.print.attribute.standard.OrientationRequested;
63import javax.print.attribute.standard.PageRanges;
64import javax.print.attribute.standard.PrinterState;
65import javax.print.attribute.standard.PrinterStateReason;
66import javax.print.attribute.standard.PrinterStateReasons;
67import javax.print.attribute.standard.Severity;
68import javax.print.attribute.standard.SheetCollate;
69import javax.print.attribute.standard.Sides;
70import javax.print.event.PrintServiceAttributeListener;
71
72
73public 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    private transient PrintServiceAttributeSet lastSet;
196    private transient 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[]= 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
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.
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[]= 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
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
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[]= PrintServiceLookupProvider.execCmd(command);
355        int qlen = (results == null) ? 0 : results.length;
356
357        return new QueuedJobCount(qlen);
358    }
359
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
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
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
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 (PrintServiceLookupProvider.isSysV()) {
472            return getSysVServiceAttributes();
473        } else if (PrintServiceLookupProvider.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}
1087