OSDN Git Service

GNU Classpath import (libgcj-snapshot-20100921).
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / javax / print / ipp / IppResponse.java
1 /* IppResponse.java --
2  Copyright (C) 2006 Free Software Foundation, Inc.
3
4  This file is part of GNU Classpath.
5
6  GNU Classpath is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2, or (at your option)
9  any later version.
10
11  GNU Classpath is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  General Public License for more details.
15
16  You should have received a copy of the GNU General Public License
17  along with GNU Classpath; see the file COPYING.  If not, write to the
18  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  02110-1301 USA.
20
21  Linking this library statically or dynamically with other modules is
22  making a combined work based on this library.  Thus, the terms and
23  conditions of the GNU General Public License cover the whole
24  combination.
25
26  As a special exception, the copyright holders of this library give you
27  permission to link this library with independent modules to produce an
28  executable, regardless of the license terms of these independent
29  modules, and to copy and distribute the resulting executable under
30  terms of your choice, provided that you also meet, for each linked
31  independent module, the terms and conditions of the license of that
32  module.  An independent module is a module which is not derived from
33  or based on this library.  If you modify this library, you may extend
34  this exception to your version of the library, but you are not
35  obligated to do so.  If you do not wish to do so, delete this
36  exception statement from your version. */
37
38
39 package gnu.javax.print.ipp;
40
41 import gnu.classpath.debug.Component;
42 import gnu.classpath.debug.SystemLogger;
43 import gnu.javax.print.ipp.attribute.UnknownAttribute;
44 import gnu.javax.print.ipp.attribute.defaults.DocumentFormatDefault;
45 import gnu.javax.print.ipp.attribute.defaults.JobHoldUntilDefault;
46 import gnu.javax.print.ipp.attribute.defaults.JobSheetsDefault;
47 import gnu.javax.print.ipp.attribute.defaults.MediaDefault;
48 import gnu.javax.print.ipp.attribute.defaults.PrinterResolutionDefault;
49 import gnu.javax.print.ipp.attribute.job.AttributesCharset;
50 import gnu.javax.print.ipp.attribute.job.AttributesNaturalLanguage;
51 import gnu.javax.print.ipp.attribute.job.JobMoreInfo;
52 import gnu.javax.print.ipp.attribute.job.JobPrinterUri;
53 import gnu.javax.print.ipp.attribute.job.JobUri;
54 import gnu.javax.print.ipp.attribute.printer.CharsetConfigured;
55 import gnu.javax.print.ipp.attribute.printer.DocumentFormat;
56 import gnu.javax.print.ipp.attribute.printer.NaturalLanguageConfigured;
57 import gnu.javax.print.ipp.attribute.printer.PrinterCurrentTime;
58 import gnu.javax.print.ipp.attribute.printer.PrinterDriverInstaller;
59 import gnu.javax.print.ipp.attribute.supported.CharsetSupported;
60 import gnu.javax.print.ipp.attribute.supported.DocumentFormatSupported;
61 import gnu.javax.print.ipp.attribute.supported.GeneratedNaturalLanguageSupported;
62 import gnu.javax.print.ipp.attribute.supported.JobHoldUntilSupported;
63 import gnu.javax.print.ipp.attribute.supported.JobSheetsSupported;
64 import gnu.javax.print.ipp.attribute.supported.MediaSupported;
65 import gnu.javax.print.ipp.attribute.supported.PrinterResolutionSupported;
66 import gnu.javax.print.ipp.attribute.supported.PrinterUriSupported;
67
68 import java.io.ByteArrayOutputStream;
69 import java.io.DataInputStream;
70 import java.io.IOException;
71 import java.io.InputStream;
72 import java.net.URI;
73 import java.net.URISyntaxException;
74 import java.util.ArrayList;
75 import java.util.Calendar;
76 import java.util.Date;
77 import java.util.HashMap;
78 import java.util.HashSet;
79 import java.util.List;
80 import java.util.Map;
81 import java.util.Set;
82 import java.util.logging.Logger;
83
84 import javax.print.attribute.Attribute;
85 import javax.print.attribute.standard.CopiesSupported;
86 import javax.print.attribute.standard.DateTimeAtCompleted;
87 import javax.print.attribute.standard.DateTimeAtCreation;
88 import javax.print.attribute.standard.DateTimeAtProcessing;
89 import javax.print.attribute.standard.JobImpressionsSupported;
90 import javax.print.attribute.standard.JobKOctetsSupported;
91 import javax.print.attribute.standard.JobMediaSheetsSupported;
92 import javax.print.attribute.standard.JobStateReason;
93 import javax.print.attribute.standard.JobStateReasons;
94 import javax.print.attribute.standard.NumberUpSupported;
95 import javax.print.attribute.standard.PrinterMoreInfo;
96 import javax.print.attribute.standard.PrinterMoreInfoManufacturer;
97 import javax.print.attribute.standard.PrinterStateReason;
98 import javax.print.attribute.standard.PrinterStateReasons;
99 import javax.print.attribute.standard.Severity;
100
101 /**
102  * <code>IppResponse</code> models a response received from an IPP
103  * compatible server as described in RFC 2910 IPP 1.1 Encoding and Transport.
104  *
105  * @author Wolfgang Baer (WBaer@gmx.de)
106  */
107 public class IppResponse
108 {
109
110   /**
111    * <code>ResponseReader</code> is responsible for parsing an IPP 1.1
112    * response stream. It provides access to the attribute groups after parsing
113    * via getter methods.
114    * <p>
115    * The enconding of a response is structured as follows (for an official
116    * description please have a look at the RFC document mentioned above):
117    * <ul>
118    * <li>version-number            - 2 bytes - required</li>
119    * <li>status-code               - 2 bytes - required</li>
120    * <li>request-id                - 4 bytes - required</li>
121    * <li>attribute-group           - n bytes - 0 or more</li>
122    * <li>end-of-attributes-tag     - 1 byte  - required</li>
123    * <li>data                      - q bytes - optional</li>
124    * </ul>
125    * </p><p>
126    * Where each attribute-group (if any) is encoded as follows:
127    * <ul>
128    * <li>begin-attribute-group-tag - 1 byte</li>
129    * <li>attribute                 - p bytes - 0 or more</li>
130    * </ul>
131    * </p><p>
132    * Encoding of attributes:
133    * <ul>
134    * <li>attribute-with-one-value - q bytes</li>
135    * <li>additional-value         - r bytes  - 0 or more</li>
136    * </ul>
137    * </p><p>
138    * Encoding of attribute-with-one-value:
139    * <ul>
140    * <li>value-tag                  - 1 byte</li>
141    * <li>name-length  (value is u)  - 2 bytes</li>
142    * <li>name                       - u bytes</li>
143    * <li>value-length  (value is v) - 2 bytes</li>
144    * <li>value                      - v bytes</li>
145    * </ul>
146    * </p><p>
147    * Encoding of additional value:
148    * <ul>
149    * <li>value-tag                       - 1 byte</li>
150    * <li>name-length  (value is 0x0000)  - 2 bytes</li>
151    * <li>value-length (value is w)       - 2 bytes</li>
152    * <li>value                           - w bytes</li>
153    * </ul>
154    * </p>
155    *
156    * @author Wolfgang Baer (WBaer@gmx.de)
157    */
158   class ResponseReader
159   {
160     /** The IPP version defaults to 1.1 */
161     private static final short VERSION = 0x0101;
162
163     /**
164      * Parses the inputstream containing the response of the IPP request.
165      * @param input the inputstream
166      * @throws IppException if unexpected exceptions occur.
167      * @throws IOException if IO problems with the underlying inputstream occur.
168      */
169     public void parseResponse(InputStream input)
170         throws IppException, IOException
171     {
172       DataInputStream stream = new DataInputStream(input);
173
174       short version = stream.readShort();
175       status_code = stream.readShort();
176       request_id = stream.readInt();
177
178       if (VERSION != version)
179         throw new IppException("Version mismatch - "
180           + "implementation does not support other versions than IPP 1.1");
181
182       logger.log(Component.IPP, "Statuscode: "
183         + Integer.toHexString(status_code) + " Request-ID: " + request_id);
184
185       byte tag = 0;
186       boolean proceed = true;
187       HashMap<Class<? extends Attribute>, Set<Attribute>> tmp;
188       // iterate over attribute-groups until end-of-attributes-tag is found
189       while (proceed)
190         {
191           if (tag == 0) // only at start time
192             tag = stream.readByte();
193
194           logger.log(Component.IPP, "DelimiterTag: " + Integer.toHexString(tag));
195
196           // check if end of attributes
197           switch (tag)
198             {
199             case IppDelimiterTag.END_OF_ATTRIBUTES_TAG:
200               proceed = false;
201               break;
202             case IppDelimiterTag.OPERATION_ATTRIBUTES_TAG:
203               tmp = new HashMap<Class<? extends Attribute>, Set<Attribute>>();
204               tag = parseAttributes(tmp, stream);
205               operationAttributes.add(tmp);
206               break;
207             case IppDelimiterTag.JOB_ATTRIBUTES_TAG:
208               tmp = new HashMap<Class<? extends Attribute>, Set<Attribute>>();
209               tag = parseAttributes(tmp, stream);
210               jobAttributes.add(tmp);
211               break;
212             case IppDelimiterTag.PRINTER_ATTRIBUTES_TAG:
213               tmp = new HashMap<Class<? extends Attribute>, Set<Attribute>>();
214               tag = parseAttributes(tmp, stream);
215               printerAttributes.add(tmp);
216               break;
217             case IppDelimiterTag.UNSUPPORTED_ATTRIBUTES_TAG:
218               System.out.println("Called");
219               tmp = new HashMap<Class<? extends Attribute>, Set<Attribute>>();
220               tag = parseAttributes(tmp, stream);
221               unsupportedAttributes.add(tmp);
222               break;
223             default:
224               throw new IppException("Unknown tag with value "
225                                      + Integer.toHexString(tag) + " occured.");
226             }
227         }
228
229       // if there are more bytes that has to be data.
230       ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
231       byte[] readbuf = new byte[2048];
232       int len = 0;
233
234       while ((len = stream.read(readbuf)) > 0)
235         byteStream.write(readbuf, 0, len);
236
237       byteStream.flush();
238       data = byteStream.toByteArray();
239     }
240
241     /**
242      * The actual parsing of the attributes and further putting into the
243      * provided group maps.
244      * @param attributes the provided attribute group map.
245      * @param stream the provided stream to read from.
246      * @return The last read tag byte (normally a DelimiterTag)
247      * @throws IppException if unexpected exceptions occur.
248      * @throws IOException if IO problems with the underlying inputstream occur.
249      */
250     private byte parseAttributes(Map<Class<? extends Attribute>, Set<Attribute>> attributes,
251                                  DataInputStream stream)
252         throws IppException, IOException
253     {
254       Attribute lastAttribute = null;
255       Attribute attribute = null;
256
257       // declaration of variables
258       short nameLength;
259       String name;
260       short valueLength;
261       byte[] value;
262
263       // tmp variables for parsing
264       // declared here so no name duplication occurs
265       URI uri;
266       String str;
267
268       while (true)
269         {
270           byte tag = stream.readByte();
271
272           if (IppDelimiterTag.isDelimiterTag(tag))
273             return tag;
274
275           // it must be a value tag now
276           // so we have either a attribute-with-one-value
277           // or (if setOf is possible) an additional-value
278
279           // (1) Length of the name
280           nameLength = stream.readShort();
281
282           // (2) The name itself
283           // may be an additional-value
284           if (nameLength == 0x0000)
285             name = lastAttribute.getName();
286           else
287             {
288               byte[] nameBytes = new byte[nameLength];
289               stream.read(nameBytes);
290               name = new String(nameBytes);
291             }
292
293           // (3) Length of the value
294           valueLength = stream.readShort();
295
296           // (4) The value itself
297           value = new byte[valueLength];
298           stream.read(value);
299
300           // the value itself
301           switch (tag)
302             {
303             // out-of-band values
304             case IppValueTag.UNSUPPORTED:
305             case IppValueTag.UNKNOWN:
306               // TODO implement out-of-band handling
307               // We currently throw an exception to see when it occurs - not yet :-)
308               throw new IppException(
309                     "Unexpected name value for out-of-band value tag " + tag);
310             case IppValueTag.NO_VALUE:
311               attribute = null;
312
313               break;
314             case IppValueTag.INTEGER:
315               int intValue = IppUtilities.convertToInt(value);
316               attribute = IppUtilities.getIntegerAttribute(name, intValue);
317
318               break;
319             case IppValueTag.BOOLEAN:
320               // JPS API models boolean syntax type as enums
321               // 0x01 = true, 0x00 = false - all are enums
322               attribute = IppUtilities.getEnumAttribute(name, new Integer(value[0]));
323
324               break;
325             case IppValueTag.ENUM:
326               int intVal = IppUtilities.convertToInt(value);
327               attribute = IppUtilities.getEnumAttribute(name, new Integer(intVal));
328
329               break;
330             case IppValueTag.OCTECTSTRING_UNSPECIFIED:
331               // none exists according to spec
332               // so lets report as exception to see when it occurs
333               throw new IppException("Unspecified octet string occured.");
334
335             case IppValueTag.DATETIME:
336               Date date = parseDate(value);
337               if (name.equals("printer-current-time"))
338                 attribute = new PrinterCurrentTime(date);
339               else if (name.equals("date-time-at-creation"))
340                 attribute = new DateTimeAtCreation(date);
341               else if (name.equals("date-time-at-processing"))
342                 attribute = new DateTimeAtProcessing(date);
343               else if (name.equals("date-time-at-completed"))
344                 attribute = new DateTimeAtCompleted(date);
345
346               break;
347             case IppValueTag.RESOLUTION:
348               int crossFeed = IppUtilities.convertToInt(value[0], value[1], value[2], value[3]);
349               int feed = IppUtilities.convertToInt(value[4], value[5], value[6], value[7]);
350               int units = value[8];
351
352               if (name.equals("printer-resolution-default"))
353                 attribute = new PrinterResolutionDefault(crossFeed, feed, units);
354               else if (name.equals("printer-resolution-supported")) // may be here also
355                 attribute = new PrinterResolutionSupported(crossFeed, feed, units);
356
357               break;
358             case IppValueTag.RANGEOFINTEGER:
359               int lower = IppUtilities.convertToInt(value[0], value[1], value[2], value[3]);
360               int upper = IppUtilities.convertToInt(value[4], value[5], value[6], value[7]);
361
362               if (name.equals("copies-supported"))
363                 attribute = new CopiesSupported(lower, upper);
364               else if (name.equals("number-up-supported"))
365                 attribute = new NumberUpSupported(lower, upper);
366               else if (name.equals("job-k-octets-supported"))
367                 attribute = new JobKOctetsSupported(lower, upper);
368               else if (name.equals("job-impressions-supported"))
369                 attribute = new JobImpressionsSupported(lower, upper);
370               else if (name.equals("job-media-sheets-supported"))
371                 attribute = new JobMediaSheetsSupported(lower, upper);
372
373               break;
374             case IppValueTag.TEXT_WITH_LANGUAGE:
375             case IppValueTag.TEXT_WITHOUT_LANGUAGE:
376             case IppValueTag.NAME_WITH_LANGUAGE:
377             case IppValueTag.NAME_WITHOUT_LANGUAGE:
378               attribute = IppUtilities.getTextAttribute(name, tag, value);
379
380               break;
381             case IppValueTag.KEYWORD:
382               str = new String(value);
383               if (name.equals("job-hold-until-supported")) // may also be name type
384                 attribute = new JobHoldUntilSupported(str, null);
385               else if (name.equals("job-hold-until-default"))
386                 attribute = new JobHoldUntilDefault(str, null);
387               else if (name.equals("media-supported"))
388                 attribute = new MediaSupported(str, null);
389               else if (name.equals("media-default"))
390                 attribute = new MediaDefault(str, null);
391               else if (name.equals("job-sheets-default"))
392                 attribute = new JobSheetsDefault(str, null);
393               else if (name.equals("job-sheets-supported"))
394                 attribute = new JobSheetsSupported(str, null);
395               else if (name.equals("job-state-reasons")) // setOf
396                 attribute = parseJobStateReasons(value, lastAttribute);
397               else if (name.equals("printer-state-reasons")) // setOf
398                 attribute = parsePrinterStateReasons(value, lastAttribute);
399               else
400                 attribute = IppUtilities.getEnumAttribute(name, str);
401
402               // all other stuff is either an enum or needs to be mapped to an
403               // UnknownAttribute instance. Enums catched here are:
404               // ipp-versions-supported, pdl-override-supported, compression-supported
405               // uri-authentication-supported, uri-security-supported, sides-supported
406               // sides-default, multiple-document-handling-supported, multiple-document-handling-default
407
408               break;
409             case IppValueTag.URI:
410               try
411                 {
412                   uri = new URI(new String(value));
413                 }
414               catch (URISyntaxException e)
415                 {
416                   throw new IppException("Wrong URI syntax encountered.", e);
417                 }
418
419               if (name.equals("job-uri"))
420                 attribute = new JobUri(uri);
421               else if (name.equals("job-printer-uri"))
422                 attribute = new JobPrinterUri(uri);
423               else if (name.equals("job-more-info"))
424                 attribute = new JobMoreInfo(uri);
425               else if (name.equals("printer-uri-supported")) // setOf
426                 attribute = new PrinterUriSupported(uri);
427               else if (name.equals("printer-more-info"))
428                 attribute = new PrinterMoreInfo(uri);
429               else if (name.equals("printer-driver-installer"))
430                 attribute = new PrinterDriverInstaller(uri);
431               else if (name.equals("printer-more-info-manufacturer"))
432                 attribute = new PrinterMoreInfoManufacturer(uri);
433
434               break;
435             case IppValueTag.URI_SCHEME:
436               // only one uri-scheme exists - and its an enum
437               if (name.equals("reference-uri-schemes-supported"))
438                 attribute = IppUtilities.getEnumAttribute(name, new String(value));
439
440               break;
441             case IppValueTag.CHARSET:
442               str = new String(value);
443               if (name.equals("attributes-charset"))
444                 attribute = new AttributesCharset(str);
445               else if (name.equals("charset-configured"))
446                 attribute = new CharsetConfigured(str);
447               else if (name.equals("charset-supported")) // setOf
448                 attribute = new CharsetSupported(str);
449
450               break;
451             case IppValueTag.NATURAL_LANGUAGE:
452               str = new String(value);
453               if (name.equals("attributes-natural-language"))
454                 attribute = new AttributesNaturalLanguage(str);
455               else if (name.equals("natural-language-configured"))
456                 attribute = new NaturalLanguageConfigured(str);
457               else if (name.equals("generated-natural-language-supported")) // setOf
458                 attribute = new GeneratedNaturalLanguageSupported(str);
459
460               break;
461             case IppValueTag.MIME_MEDIA_TYPE:
462               str = new String(value);
463               if (name.equals("document-format-default"))
464                 attribute = new DocumentFormatDefault(str, null);
465               else if (name.equals("document-format-supported")) // setOf
466                 attribute = new DocumentFormatSupported(str, null);
467               else if (name.equals("document-format")) // setOf
468                 attribute = new DocumentFormat(str, null);
469
470               break;
471             default:
472               throw new IppException("Unknown tag with value "
473                                      + Integer.toHexString(tag) + " found.");
474             }
475
476           if (attribute == null)
477             attribute = new UnknownAttribute(tag, name, value);
478
479           addAttribute(attributes, attribute);
480           lastAttribute = attribute;
481
482           logger.log(Component.IPP, "Attribute: " + name
483                      + " Value: " + attribute.toString());
484         }
485     }
486
487     /**
488      * Adds a new attribute to the given attribute group. If this is the fist
489      * occurence of this attribute category a new set is created and associated
490      * with its category as key.
491      * @param attributeGroup
492      *          the attribute group
493      * @param attribute
494      *          the attribute to add
495      */
496     private void addAttribute(Map<Class<? extends Attribute>, Set<Attribute>> attributeGroup,
497                               Attribute attribute)
498     {
499       Class<? extends Attribute> clazz = attribute.getCategory();
500       Set<Attribute> attributeValues = attributeGroup.get(clazz);
501
502       if (attributeValues == null) // first attribute of this category
503         {
504           attributeValues = new HashSet<Attribute>();
505           attributeGroup.put(clazz, attributeValues);
506         }
507
508       attributeValues.add(attribute);
509     }
510
511     /**
512      * Parses a name with or without language attribute value from the byte[]
513      * and returns the result as an object[].
514      * @param value the byte[]
515      * @param lastAttr the last attribute
516      * @return The attribute.
517      */
518     private PrinterStateReasons parsePrinterStateReasons(byte[] value, Attribute lastAttr)
519     {
520       String str = new String(value);
521       PrinterStateReasons attribute;
522
523       if (lastAttr instanceof PrinterStateReasons)
524         attribute = (PrinterStateReasons) lastAttr;
525       else
526         attribute = new PrinterStateReasons();
527
528       // special case indicating no reasons
529       if (str.equals("none"))
530         return attribute;
531
532       Severity severity = null;
533       PrinterStateReason reason = null;
534
535       if (str.endsWith(Severity.WARNING.toString()))
536         severity = Severity.WARNING;
537       else if (str.endsWith(Severity.REPORT.toString()))
538         severity = Severity.REPORT;
539       else if (str.endsWith(Severity.ERROR.toString()))
540         severity = Severity.ERROR;
541
542       if (severity != null)
543         str = str.substring(0, str.lastIndexOf('-'));
544       else // we must associate a severity
545         severity = Severity.REPORT;
546
547       reason = (PrinterStateReason)
548         IppUtilities.getEnumAttribute("printer-state-reason", str);
549
550       attribute.put(reason , severity);
551       return attribute;
552     }
553
554     /**
555      * Parses a name with or without language attribute value from the byte[]
556      * and returns the result as an object[].
557      * @param value the byte[]
558      * @param lastAttr the last attribute
559      * @return The attribute.
560      */
561     private JobStateReasons parseJobStateReasons(byte[] value, Attribute lastAttr)
562     {
563       String str = new String(value);
564       JobStateReasons attribute;
565
566       if (lastAttr instanceof JobStateReasons)
567         attribute = (JobStateReasons) lastAttr;
568       else
569         attribute = new JobStateReasons();
570
571       // special case indicating no reasons
572       if (str.equals("none"))
573         return attribute;
574
575       JobStateReason reason = (JobStateReason)
576         IppUtilities.getEnumAttribute("job-state-reason", str);
577
578       attribute.add(reason);
579       return attribute;
580     }
581
582     /**
583      * Parses a DateTime syntax attribute and returns the constructed Date
584      * object.
585      * <p>
586      * The syntax value is defined as 11 octets follwing the DateAndTime format
587      * of RFC 1903:
588      * <ul>
589      * <li>field | octets | contents | range</li>
590      * <li>1 | 1-2 | year | 0..65536</li>
591      * <li>2 | 3 | month | 1..12</li>
592      * <li>3 | 4 | day | 1..31</li>
593      * <li>4 | 5 | hour | 0..23</li>
594      * <li>5 | 6 | minutes | 0..59</li>
595      * <li>6 | 7 | seconds | 0..60 (use 60 for leap-second)</li>
596      * <li>7 | 8 | deci-seconds | 0..9</li>
597      * <li>8 | 9 | direction from UTC | '+' / '-'</li>
598      * <li>9 | 10 | hours from UTC | 0..11</li>
599      * <li>10 | 11 | minutes from UTC | 0..59</li>
600      * </ul>
601      * </p>
602      *
603      * @param value the byte[]
604      * @return The date object.
605      */
606     private Date parseDate(byte[] value)
607     {
608       short year = IppUtilities.convertToShort(value[0], value[1]);
609
610       Calendar cal = Calendar.getInstance();
611       cal.set(Calendar.YEAR, year);
612       cal.set(Calendar.MONTH, value[2]);
613       cal.set(Calendar.DAY_OF_MONTH, value[3]);
614       cal.set(Calendar.HOUR_OF_DAY, value[4]);
615       cal.set(Calendar.MINUTE, value[5]);
616       cal.set(Calendar.SECOND, value[6]);
617       cal.set(Calendar.MILLISECOND, value[7] * 100); // deci-seconds
618
619       // offset from timezone
620       int offsetMilli = value[9] * 3600000; // hours to millis
621       offsetMilli = offsetMilli + value[10] * 60000; // minutes to millis
622
623       if (((char) value[8]) == '-')
624         offsetMilli = offsetMilli * (-1);
625
626       cal.set(Calendar.ZONE_OFFSET, offsetMilli);
627       return cal.getTime();
628     }
629   }
630
631   /**
632    * Logger for tracing - enable by passing
633    * -Dgnu.classpath.debug.components=ipp to the vm.
634    */
635   static final Logger logger = SystemLogger.SYSTEM;
636
637   URI uri;
638   short operation_id;
639   short status_code;
640   int request_id;
641
642   List<Map<Class<? extends Attribute>, Set<Attribute>>> operationAttributes;
643   List<Map<Class<? extends Attribute>, Set<Attribute>>> printerAttributes;
644   List<Map<Class<? extends Attribute>, Set<Attribute>>> jobAttributes;
645   List<Map<Class<? extends Attribute>, Set<Attribute>>> unsupportedAttributes;
646
647   byte[] data;
648
649   /**
650    * Creates an <code>IppResponse</code> instance.
651    *
652    * @param uri the uri the request was directy to.
653    * @param operation_id the operation id of the request.
654    */
655   public IppResponse(URI uri, short operation_id)
656   {
657     this.uri = uri;
658     this.operation_id = operation_id;
659     operationAttributes =
660       new ArrayList<Map<Class<? extends Attribute>, Set<Attribute>>>();
661     jobAttributes =
662       new ArrayList<Map<Class<? extends Attribute>, Set<Attribute>>>();
663     printerAttributes =
664       new ArrayList<Map<Class<? extends Attribute>, Set<Attribute>>>();
665     unsupportedAttributes =
666       new ArrayList<Map<Class<? extends Attribute>, Set<Attribute>>>();
667   }
668
669   /**
670    * Sets the data received from the request sent.
671    *
672    * @param input the input stream received.
673    * @throws IppException if parsing fails.
674    */
675   protected void setResponseData(InputStream input) throws IppException
676   {
677     ResponseReader reader = new ResponseReader();
678
679     try
680       {
681         reader.parseResponse(input);
682       }
683     catch (IOException e)
684       {
685         throw new IppException(
686             "Exception during response parsing caused by IOException", e);
687       }
688   }
689
690   /**
691    * Returns the uri of the original request.
692    * @return The URI of the request.
693    */
694   public URI getURI()
695   {
696     return uri;
697   }
698
699   /**
700    * Returns the operation id of the original request.
701    * @return The operation id of the request.
702    */
703   public int getOperationID()
704   {
705     return operation_id;
706   }
707
708   /**
709    * Returns the set of job attributes group maps.
710    * There may occur more than one group of type job attribute in a response
711    * because of e.g. multiple job or print service informations requested.
712    *
713    * @return The list of job attribute group maps.
714    */
715   public List<Map<Class<? extends Attribute>, Set<Attribute>>> getJobAttributes()
716   {
717     return jobAttributes;
718   }
719
720   /**
721    * Returns the set of operation attributes group maps.
722    * There may occur more than one group of type job attribute in a response
723    * because of e.g. multiple job or print service informations requested.
724    *
725    * @return The list of operation attribute group maps.
726    */
727   public List<Map<Class<? extends Attribute>, Set<Attribute>>> getOperationAttributes()
728   {
729     return operationAttributes;
730   }
731
732   /**
733    * Returns the set of printer attributes group maps.
734    * There may occur more than one group of type job attribute in a response
735    * because of e.g. multiple job or print service informations requested.
736    *
737    * @return The list of printer attribute group maps.
738    */
739   public List<Map<Class<? extends Attribute>, Set<Attribute>>> getPrinterAttributes()
740   {
741     return printerAttributes;
742   }
743
744   /**
745    * Returns the ID of the initial request.
746    *
747    * @return The request ID.
748    */
749   public int getRequestID()
750   {
751     return request_id;
752   }
753
754   /**
755    * Returns the status code of the response.
756    * Defined in {@link IppStatusCode}.
757    *
758    * @return The status code.
759    */
760   public short getStatusCode()
761   {
762     return status_code;
763   }
764
765   /**
766    * Returns the set of unsupported attributes group maps.
767    * There may occur more than one group of type job attribute in a response
768    * because of e.g. multiple job or print service informations requested.
769    *
770    * @return The list of unsupported attribute group maps.
771    */
772   public List<Map<Class<? extends Attribute>, Set<Attribute>>> getUnsupportedAttributes()
773   {
774     return unsupportedAttributes;
775   }
776
777   /**
778    * Returns the data of the response.
779    *
780    * @return The data as byte[].
781    */
782   public byte[] getData()
783   {
784     return data;
785   }
786
787 }