2 * Copyright (C) 2003 <a href="http://www.lohndirekt.de/">lohndirekt.de</a>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 package de
.lohndirekt
.print
.attribute
;
21 import java
.io
.IOException
;
22 import java
.io
.InputStream
;
23 import java
.lang
.reflect
.Constructor
;
24 import java
.lang
.reflect
.Field
;
25 import java
.lang
.reflect
.InvocationTargetException
;
27 import java
.net
.URISyntaxException
;
28 import java
.text
.DateFormat
;
29 import java
.text
.DecimalFormat
;
30 import java
.text
.ParseException
;
31 import java
.text
.SimpleDateFormat
;
32 import java
.util
.Date
;
33 import java
.util
.HashMap
;
34 import java
.util
.HashSet
;
35 import java
.util
.Locale
;
38 import java
.util
.logging
.Level
;
39 import java
.util
.logging
.Logger
;
41 import javax
.print
.attribute
.Attribute
;
42 import javax
.print
.attribute
.EnumSyntax
;
43 import javax
.print
.attribute
.standard
.JobStateReason
;
44 import javax
.print
.attribute
.standard
.JobStateReasons
;
45 import javax
.print
.attribute
.standard
.PrinterStateReason
;
46 import javax
.print
.attribute
.standard
.PrinterStateReasons
;
47 import javax
.print
.attribute
.standard
.Severity
;
49 import de
.lohndirekt
.print
.attribute
.ipp
.Charset
;
50 import de
.lohndirekt
.print
.attribute
.ipp
.jobdesc
.LdJobStateReason
;
51 import de
.lohndirekt
.print
.exception
.EndOfAttributesException
;
57 public final class AttributeParser
{
59 private final static EndOfAttributesException END_OF_ATTRIBUTES_EXCEPTION
= new EndOfAttributesException();
61 private final static Logger log
= Logger
.getLogger(AttributeParser
.class.getName());
70 private static Attribute
getAttribute(String name
, Object
[] values
) {
71 Attribute attribute
= null;
72 IppAttributeName attrName
= IppAttributeName
.get(name
);
73 Class attrClass
= attrName
.getAttributeClass();
74 Class superClass
= attrClass
.getSuperclass();
75 if (superClass
!= null) {
76 if (superClass
.equals(EnumSyntax
.class)) {
78 Field
[] fields
= attrClass
.getDeclaredFields();
79 for (int i
= 0; i
< fields
.length
; i
++) {
80 Field field
= fields
[i
];
81 if (field
.getType().equals(attrClass
)) {
82 EnumSyntax attr
= (EnumSyntax
) field
.get(null);
83 if (values
[0] instanceof String
) {
84 if (attr
.toString().equals(values
[0])) {
85 attribute
= (Attribute
) attr
;
89 if (attr
.getValue() == ((Integer
) values
[0]).intValue()) {
90 attribute
= (Attribute
) attr
;
96 } catch (SecurityException e
) {
97 log
.log(Level
.SEVERE
, e
.getMessage(), e
);
98 } catch (IllegalArgumentException e
) {
99 log
.log(Level
.SEVERE
, e
.getMessage(), e
);
100 } catch (IllegalAccessException e
) {
101 log
.log(Level
.SEVERE
, e
.getMessage(), e
);
105 Class
[] parameters
= toClassArray(values
);
107 Constructor constructor
= attrClass
.getDeclaredConstructor(parameters
);
108 attribute
= (Attribute
) constructor
.newInstance(values
);
109 } catch (SecurityException e
) {
110 log
.log(Level
.SEVERE
, e
.getMessage(), e
);
111 } catch (NoSuchMethodException e
) {
112 log
.log(Level
.SEVERE
, e
.getMessage(), e
);
113 } catch (IllegalArgumentException e
) {
114 log
.log(Level
.SEVERE
, e
.getMessage(), e
);
115 } catch (InstantiationException e
) {
116 log
.log(Level
.SEVERE
, e
.getMessage(), e
);
117 } catch (IllegalAccessException e
) {
118 log
.log(Level
.SEVERE
, e
.getMessage(), e
);
119 } catch (InvocationTargetException e
) {
120 log
.log(Level
.SEVERE
, e
.getMessage(), e
);
131 * @param lastAttribute
134 private static Attribute
parseAttribute(InputStream in
, Attribute lastAttribute
)
135 throws IOException
, EndOfAttributesException
{
138 while ((valueTag
= in
.read()) < IppValueTag
.UNSUPPORTED_VALUE
.getValue()) {
139 if (valueTag
== IppDelimiterTag
.END_ATTRIBUTES
.getValue()) {
140 throw END_OF_ATTRIBUTES_EXCEPTION
;
144 int nameLength
= parseInt2(in
);
145 // parse the Attribute-Name
147 if (nameLength
== 0) {
148 name
= lastAttribute
.getName();
150 name
= parseString(in
, nameLength
);
154 Object
[] values
= parseValues(valueTag
, in
);
156 if (name
.equals(IppAttributeName
.PRINTER_STATE_REASONS
.getName())) {
157 return parsePrinterStateReasons((String
) values
[0], lastAttribute
);
158 } else if (name
.equals(IppAttributeName
.JOB_STATE_REASONS
.getName())) {
159 return parseJobStateReasons(values
, lastAttribute
);
161 return getAttribute(name
, values
);
165 private static String
parseString(InputStream in
, int nameLength
) throws IOException
{
166 return parseString(in
, nameLength
, Charset
.US_ASCII
.getValue());
169 private static String
parseNameAndTextString(InputStream in
, int nameLength
) throws IOException
{
170 return parseString(in
, nameLength
, AttributeWriter
.DEFAULT_CHARSET
.getValue());
177 private static String
parseString(InputStream in
, int nameLength
, String charsetName
) throws IOException
{
178 byte[] bytes
= new byte[nameLength
];
180 return new String(bytes
, charsetName
);
192 private static Date
parseDate(InputStream in
) throws IOException
{
193 DecimalFormat twoDigits
= new DecimalFormat("00");
194 DecimalFormat threeDigits
= new DecimalFormat("000");
195 DecimalFormat fourDigits
= new DecimalFormat("0000");
196 //year is encoded in network-byte order
197 int year
= parseInt2(in
);
198 int month
= in
.read();
200 int hour
= in
.read();
201 int minute
= in
.read();
202 int second
= in
.read();
203 int deci
= in
.read();
204 int mili
= deci
* 100;
205 char direction
= (char) in
.read();
206 int hoursFromUtc
= (int) in
.read();
207 int minutesFromUtc
= (int) in
.read();
209 String yearString
= fourDigits
.format((long) year
);
210 String monthString
= twoDigits
.format((long) month
);
211 String dayString
= twoDigits
.format((long) day
);
212 String hourString
= twoDigits
.format((long) hour
);
213 String minuteString
= twoDigits
.format((long) minute
);
214 String secondString
= twoDigits
.format((long) second
);
215 String miliString
= threeDigits
.format((long) mili
);
216 String timeZone
= direction
+ twoDigits
.format((long) hoursFromUtc
) + twoDigits
.format((long) minutesFromUtc
);
232 DateFormat dateFormat
= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSSZ");
235 date
= dateFormat
.parse(dateString
);
236 } catch (ParseException e
) {
237 log
.log(Level
.SEVERE
, e
.getMessage(), e
);
242 private static int parseInt4(InputStream in
) throws IOException
{
244 //Same parsing as in java.io.DataInput readInt()
249 int value
= (((a
& 0xff) << 24) | ((b
& 0xff) << 16) | ((c
& 0xff) << 8) | (d
& 0xff));
258 private static int parseInt4(byte[] bytes
, int offset
) {
260 //Same parsing as in java.io.DataInput readInt()
261 byte a
= bytes
[offset
++];
262 byte b
= bytes
[offset
++];
263 byte c
= bytes
[offset
++];
264 byte d
= bytes
[offset
++];
265 int value
= (((a
& 0xff) << 24) | ((b
& 0xff) << 16) | ((c
& 0xff) << 8) | (d
& 0xff));
269 private static int parseInt2(InputStream in
) throws IOException
{
271 //Same parsing as in java.io.DataInput readInt()
274 int value
= ((a
& 0xff) << 8) | ((b
& 0xff));
282 * @param lastAttribute
285 private static Attribute
parseJobStateReasons(Object
[] values
, Attribute lastAttribute
) {
286 JobStateReasons reasons
;
287 if (lastAttribute
instanceof JobStateReasons
) {
288 reasons
= (JobStateReasons
) lastAttribute
;
290 reasons
= new JobStateReasons();
292 JobStateReason reason
= null;
293 if (values
[0].equals(LdJobStateReason
.NONE
.toString())) {
294 reason
= LdJobStateReason
.NONE
;
296 reason
= (JobStateReason
) getAttribute(IppAttributeName
.JOB_STATE_REASON
.getName(), values
);
303 * @param reasonAndSeverity
304 * @param lastAttribute
307 private static PrinterStateReasons
parsePrinterStateReasons(String reasonAndSeverity
, Attribute lastAttribute
) {
308 Severity severity
= null;
309 int severityOffset
= 0;
310 if ((severityOffset
= reasonAndSeverity
.indexOf(Severity
.ERROR
.toString())) > 0) {
311 severity
= Severity
.ERROR
;
312 } else if ((severityOffset
= reasonAndSeverity
.indexOf(Severity
.REPORT
.toString())) > 0) {
313 severity
= Severity
.REPORT
;
314 } else if ((severityOffset
= reasonAndSeverity
.indexOf(Severity
.WARNING
.toString())) > 0) {
315 severity
= Severity
.WARNING
;
318 if (severityOffset
!= -1) {
319 //subtract 1 for the hyphen
321 reasonString
= reasonAndSeverity
.substring(0, severityOffset
- 1);
323 reasonString
= reasonAndSeverity
;
325 Object
[] values
= new Object
[] { reasonString
};
326 PrinterStateReason reason
=
327 (PrinterStateReason
) getAttribute(IppAttributeName
.PRINTER_STATE_REASON
.getName(), values
);
328 PrinterStateReasons reasons
;
329 if (lastAttribute
instanceof PrinterStateReasons
) {
330 reasons
= (PrinterStateReasons
) lastAttribute
;
332 reasons
= new PrinterStateReasons();
334 if (reason
!= null) {
335 if (severity
== null) {
336 severity
= Severity
.ERROR
;
338 reasons
.put(reason
, severity
);
345 * @return map of attributes (key -> category, value -> Set with attributes)
349 public static Map
parseResponse(InputStream response
) throws IOException
{
350 Map attributes
= new HashMap();
351 long start
= System
.currentTimeMillis();
352 Attribute lastAttribute
= null;
353 boolean finished
= false;
357 Attribute attribute
= null;
359 attribute
= parseAttribute(response
, lastAttribute
);
360 if (attribute
!= null) {
361 lastAttribute
= attribute
;
362 attributes
= put(attributes
, attribute
);
363 if (log
.isLoggable(Level
.FINEST
)) {
364 log
.finest("parsed attribute(" + attribute
.getName() + "): " + attribute
.toString());
367 if (log
.isLoggable(Level
.FINEST
)) {
369 log
.finest("Attribute was null");
372 } catch (EndOfAttributesException e
) {
375 if (log
.isLoggable(Level
.INFO
)) {
376 log
.info("--- Attribute parsing finished ---");
380 long end
= System
.currentTimeMillis();
381 if (log
.isLoggable(Level
.INFO
)) {
382 log
.info("Parsing took " + (end
- start
) + "ms.");
394 private static URI
parseUri(InputStream in
, int valueLength
) throws IOException
{
395 String uriString
= parseString(in
, valueLength
);
398 uri
= new URI(uriString
);
399 } catch (URISyntaxException e
) {
400 throw new RuntimeException(e
);
413 private static Object
[] parseValues(int valueTag
, InputStream in
) throws IOException
{
414 int valueLength
= parseInt2(in
);
415 Object
[] values
= null;
416 if (valueTag
== IppValueTag
.INTEGER
.getValue() || valueTag
== IppValueTag
.ENUM
.getValue()) {
417 Integer number
= new Integer(parseInt4(in
));
418 values
= new Object
[] { number
};
420 valueTag
== IppValueTag
.STRING
.getValue()
421 || valueTag
== IppValueTag
.TEXT
.getValue()
422 || valueTag
== IppValueTag
.NAME
.getValue()){
423 String word
= parseNameAndTextString(in
, valueLength
);
424 values
= new Object
[] { word
, Locale
.getDefault()};
426 valueTag
== IppValueTag
.CHARSET
.getValue()
427 || valueTag
== IppValueTag
.LANGUAGE
.getValue()
428 || valueTag
== IppValueTag
.MIMETYPE
.getValue()) {
429 String word
= parseString(in
, valueLength
);
430 values
= new Object
[] { word
, Locale
.getDefault()};
431 } else if (valueTag
== IppValueTag
.URI
.getValue()) {
432 URI uri
= parseUri(in
, valueLength
);
433 values
= new Object
[] { uri
};
434 } else if (valueTag
== IppValueTag
.KEYWORD
.getValue()) {
435 String word
= parseString(in
, valueLength
);
436 values
= new Object
[] { word
, Locale
.getDefault()};
437 } else if (valueTag
== IppValueTag
.BOOLEAN
.getValue()) {
438 Integer bool
= new Integer(in
.read());
439 values
= new Object
[] { bool
};
440 } else if (valueTag
== IppValueTag
.RANGE
.getValue()) {
441 Integer lowerBound
= new Integer(parseInt4(in
));
442 Integer upperBound
= new Integer(parseInt4(in
));
443 values
= new Object
[] { lowerBound
, upperBound
};
444 } else if (valueTag
== IppValueTag
.DATE
.getValue()) {
446 Date date
= parseDate(in
);
447 values
= new Object
[] { date
};
448 } else if (valueTag
== IppValueTag
.NOVALUE
.getValue()) {
449 values
= new Object
[] {
452 throw new IllegalArgumentException("\"" + Integer
.toHexString(valueTag
) + "\" is not a valid value-tag");
462 private static Map
put(Map attributes
, Attribute attribute
) {
463 Set values
= (Set
) attributes
.get(attribute
.getCategory());
464 if (values
== null) {
465 values
= new HashSet();
466 attributes
.put(attribute
.getCategory(), values
);
468 values
.add(attribute
);
476 private static Class
[] toClassArray(Object
[] values
) {
477 Class
[] classes
= new Class
[values
.length
];
478 for (int i
= 0; i
< values
.length
; i
++) {
479 Class clazz
= values
[i
].getClass();
480 if (clazz
.equals(Integer
.class)) {