Initial import.
[clinton/mirror/jspi/.git] / jspi / src / main / java / de / lohndirekt / print / attribute / AttributeParser.java
1 /**
2 * Copyright (C) 2003 <a href="http://www.lohndirekt.de/">lohndirekt.de</a>
3 *
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.
8 *
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.
13 *
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
17 *
18 */
19 package de.lohndirekt.print.attribute;
20
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;
26 import java.net.URI;
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;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.logging.Level;
39 import java.util.logging.Logger;
40
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;
48
49 import de.lohndirekt.print.attribute.ipp.Charset;
50 import de.lohndirekt.print.attribute.ipp.jobdesc.LdJobStateReason;
51 import de.lohndirekt.print.exception.EndOfAttributesException;
52
53 /**
54 * @author bpusch
55 *
56 */
57 public final class AttributeParser {
58
59 private final static EndOfAttributesException END_OF_ATTRIBUTES_EXCEPTION = new EndOfAttributesException();
60
61 private final static Logger log = Logger.getLogger(AttributeParser.class.getName());
62
63
64
65 /**
66 * @param name
67 * @param values
68 * @return
69 */
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)) {
77 try {
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;
86 break;
87 }
88 } else {
89 if (attr.getValue() == ((Integer) values[0]).intValue()) {
90 attribute = (Attribute) attr;
91 break;
92 }
93 }
94 }
95 }
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);
102 }
103
104 } else {
105 Class[] parameters = toClassArray(values);
106 try {
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);
121 }
122 }
123 }
124
125 return attribute;
126 }
127
128 /**
129 * @param byteArray
130 * @param byteCount
131 * @param lastAttribute
132 * @return
133 */
134 private static Attribute parseAttribute(InputStream in, Attribute lastAttribute)
135 throws IOException, EndOfAttributesException {
136
137 int valueTag;
138 while ((valueTag = in.read()) < IppValueTag.UNSUPPORTED_VALUE.getValue()) {
139 if (valueTag == IppDelimiterTag.END_ATTRIBUTES.getValue()) {
140 throw END_OF_ATTRIBUTES_EXCEPTION;
141 }
142 }
143
144 int nameLength = parseInt2(in);
145 // parse the Attribute-Name
146 String name;
147 if (nameLength == 0) {
148 name = lastAttribute.getName();
149 } else {
150 name = parseString(in, nameLength);
151 }
152
153
154 Object[] values = parseValues(valueTag, in);
155
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);
160 } else {
161 return getAttribute(name, values);
162 }
163 }
164
165 private static String parseString(InputStream in, int nameLength) throws IOException{
166 return parseString(in, nameLength, Charset.US_ASCII.getValue());
167 }
168
169 private static String parseNameAndTextString(InputStream in, int nameLength) throws IOException{
170 return parseString(in, nameLength, AttributeWriter.DEFAULT_CHARSET.getValue());
171 }
172
173 /**
174 * @param in
175 * @param nameLength
176 */
177 private static String parseString(InputStream in, int nameLength, String charsetName) throws IOException {
178 byte[] bytes = new byte[nameLength];
179 in.read(bytes);
180 return new String(bytes, charsetName);
181
182 }
183
184
185
186
187 /**
188 * @param byteArray
189 * @param valueOffset
190 * @return
191 */
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();
199 int day = 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();
208
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);
217 String dateString =
218 yearString
219 + "-"
220 + monthString
221 + "-"
222 + dayString
223 + " "
224 + hourString
225 + ":"
226 + minuteString
227 + ":"
228 + secondString
229 + ":"
230 + miliString
231 + timeZone;
232 DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSSZ");
233 Date date = null;
234 try {
235 date = dateFormat.parse(dateString);
236 } catch (ParseException e) {
237 log.log(Level.SEVERE, e.getMessage(), e);
238 }
239 return date;
240 }
241
242 private static int parseInt4(InputStream in) throws IOException {
243
244 //Same parsing as in java.io.DataInput readInt()
245 int a = in.read();
246 int b = in.read();
247 int c = in.read();
248 int d = in.read();
249 int value = (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff));
250 return value;
251 }
252
253 /**
254 * @param bytes
255 * @param offset
256 * @return
257 */
258 private static int parseInt4(byte[] bytes, int offset) {
259
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));
266 return value;
267 }
268
269 private static int parseInt2(InputStream in) throws IOException {
270
271 //Same parsing as in java.io.DataInput readInt()
272 int a = in.read();
273 int b = in.read();
274 int value = ((a & 0xff) << 8) | ((b & 0xff));
275 return value;
276 }
277
278
279
280 /**
281 * @param string
282 * @param lastAttribute
283 * @return
284 */
285 private static Attribute parseJobStateReasons(Object[] values, Attribute lastAttribute) {
286 JobStateReasons reasons;
287 if (lastAttribute instanceof JobStateReasons) {
288 reasons = (JobStateReasons) lastAttribute;
289 } else {
290 reasons = new JobStateReasons();
291 }
292 JobStateReason reason = null;
293 if (values[0].equals(LdJobStateReason.NONE.toString())) {
294 reason = LdJobStateReason.NONE;
295 } else {
296 reason = (JobStateReason) getAttribute(IppAttributeName.JOB_STATE_REASON.getName(), values);
297 }
298 reasons.add(reason);
299 return reasons;
300 }
301
302 /**
303 * @param reasonAndSeverity
304 * @param lastAttribute
305 * @return
306 */
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;
316 }
317 String reasonString;
318 if (severityOffset != -1) {
319 //subtract 1 for the hyphen
320 severityOffset--;
321 reasonString = reasonAndSeverity.substring(0, severityOffset - 1);
322 } else {
323 reasonString = reasonAndSeverity;
324 }
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;
331 } else {
332 reasons = new PrinterStateReasons();
333 }
334 if (reason != null) {
335 if (severity == null) {
336 severity = Severity.ERROR;
337 }
338 reasons.put(reason, severity);
339 }
340 return reasons;
341 }
342
343 /**
344 * @param bytes
345 * @return map of attributes (key -> category, value -> Set with attributes)
346 *
347 *
348 */
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;
354 response.read();
355
356 while (!finished) {
357 Attribute attribute = null;
358 try {
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());
365 }
366 } else {
367 if (log.isLoggable(Level.FINEST)) {
368
369 log.finest("Attribute was null");
370 }
371 }
372 } catch (EndOfAttributesException e) {
373
374 finished = true;
375 if (log.isLoggable(Level.INFO)) {
376 log.info("--- Attribute parsing finished ---");
377 }
378 }
379 }
380 long end = System.currentTimeMillis();
381 if (log.isLoggable(Level.INFO)) {
382 log.info("Parsing took " + (end - start) + "ms.");
383 }
384 return attributes;
385 }
386
387
388 /**
389 * @param byteArray
390 * @param valueOffset
391 * @param valueLength
392 * @return
393 */
394 private static URI parseUri(InputStream in, int valueLength) throws IOException {
395 String uriString = parseString(in, valueLength);
396 URI uri = null;
397 try {
398 uri = new URI(uriString);
399 } catch (URISyntaxException e) {
400 throw new RuntimeException(e);
401 }
402 return uri;
403 }
404
405
406 /**
407 * @param valueTag
408 * @param byteArray
409 * @param valueOffset
410 * @param valueLength
411 * @return
412 */
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 };
419 } else if (
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()};
425 } else if (
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()) {
445
446 Date date = parseDate(in);
447 values = new Object[] { date };
448 } else if (valueTag == IppValueTag.NOVALUE.getValue()) {
449 values = new Object[] {
450 };
451 } else {
452 throw new IllegalArgumentException("\"" + Integer.toHexString(valueTag) + "\" is not a valid value-tag");
453 }
454 return values;
455 }
456
457
458 /**
459 * @param attributes
460 * @param attribute
461 */
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);
467 }
468 values.add(attribute);
469 return attributes;
470 }
471
472 /**
473 * @param values
474 * @return
475 */
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)) {
481 clazz = int.class;
482 }
483 classes[i] = clazz;
484 }
485 return classes;
486 }
487
488 }