IppResponse.java 29 KB

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