Initial import.
[clinton/mirror/jspi/.git] / jspi / src / main / java / de / lohndirekt / print / attribute / AttributeParser.java
CommitLineData
3ea135bb 1/**\r
2 * Copyright (C) 2003 <a href="http://www.lohndirekt.de/">lohndirekt.de</a>\r
3 *\r
4 * This library is free software; you can redistribute it and/or\r
5 * modify it under the terms of the GNU Lesser General Public\r
6 * License as published by the Free Software Foundation; either\r
7 * version 2.1 of the License, or (at your option) any later version.\r
8 * \r
9 * This library is distributed in the hope that it will be useful,\r
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
12 * Lesser General Public License for more details.\r
13 * \r
14 * You should have received a copy of the GNU Lesser General Public\r
15 * License along with this library; if not, write to the Free Software\r
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
17 * \r
18 */\r
19package de.lohndirekt.print.attribute;\r
20\r
21import java.io.IOException;\r
22import java.io.InputStream;\r
23import java.lang.reflect.Constructor;\r
24import java.lang.reflect.Field;\r
25import java.lang.reflect.InvocationTargetException;\r
26import java.net.URI;\r
27import java.net.URISyntaxException;\r
28import java.text.DateFormat;\r
29import java.text.DecimalFormat;\r
30import java.text.ParseException;\r
31import java.text.SimpleDateFormat;\r
32import java.util.Date;\r
33import java.util.HashMap;\r
34import java.util.HashSet;\r
35import java.util.Locale;\r
36import java.util.Map;\r
37import java.util.Set;\r
38import java.util.logging.Level;\r
39import java.util.logging.Logger;\r
40\r
41import javax.print.attribute.Attribute;\r
42import javax.print.attribute.EnumSyntax;\r
43import javax.print.attribute.standard.JobStateReason;\r
44import javax.print.attribute.standard.JobStateReasons;\r
45import javax.print.attribute.standard.PrinterStateReason;\r
46import javax.print.attribute.standard.PrinterStateReasons;\r
47import javax.print.attribute.standard.Severity;\r
48\r
49import de.lohndirekt.print.attribute.ipp.Charset;\r
50import de.lohndirekt.print.attribute.ipp.jobdesc.LdJobStateReason;\r
51import de.lohndirekt.print.exception.EndOfAttributesException;\r
52\r
53/**\r
54 * @author bpusch\r
55 *\r
56 */\r
57public final class AttributeParser {\r
58\r
59 private final static EndOfAttributesException END_OF_ATTRIBUTES_EXCEPTION = new EndOfAttributesException();\r
60\r
61 private final static Logger log = Logger.getLogger(AttributeParser.class.getName());\r
62\r
63 \r
64\r
65 /**\r
66 * @param name\r
67 * @param values\r
68 * @return\r
69 */\r
70 private static Attribute getAttribute(String name, Object[] values) {\r
71 Attribute attribute = null;\r
72 IppAttributeName attrName = IppAttributeName.get(name);\r
73 Class attrClass = attrName.getAttributeClass();\r
74 Class superClass = attrClass.getSuperclass();\r
75 if (superClass != null) {\r
76 if (superClass.equals(EnumSyntax.class)) {\r
77 try {\r
78 Field[] fields = attrClass.getDeclaredFields();\r
79 for (int i = 0; i < fields.length; i++) {\r
80 Field field = fields[i];\r
81 if (field.getType().equals(attrClass)) {\r
82 EnumSyntax attr = (EnumSyntax) field.get(null);\r
83 if (values[0] instanceof String) {\r
84 if (attr.toString().equals(values[0])) {\r
85 attribute = (Attribute) attr;\r
86 break;\r
87 }\r
88 } else {\r
89 if (attr.getValue() == ((Integer) values[0]).intValue()) {\r
90 attribute = (Attribute) attr;\r
91 break;\r
92 }\r
93 }\r
94 }\r
95 }\r
96 } catch (SecurityException e) {\r
97 log.log(Level.SEVERE, e.getMessage(), e);\r
98 } catch (IllegalArgumentException e) {\r
99 log.log(Level.SEVERE, e.getMessage(), e);\r
100 } catch (IllegalAccessException e) {\r
101 log.log(Level.SEVERE, e.getMessage(), e);\r
102 }\r
103\r
104 } else {\r
105 Class[] parameters = toClassArray(values);\r
106 try {\r
107 Constructor constructor = attrClass.getDeclaredConstructor(parameters);\r
108 attribute = (Attribute) constructor.newInstance(values);\r
109 } catch (SecurityException e) {\r
110 log.log(Level.SEVERE, e.getMessage(), e);\r
111 } catch (NoSuchMethodException e) {\r
112 log.log(Level.SEVERE, e.getMessage(), e);\r
113 } catch (IllegalArgumentException e) {\r
114 log.log(Level.SEVERE, e.getMessage(), e);\r
115 } catch (InstantiationException e) {\r
116 log.log(Level.SEVERE, e.getMessage(), e);\r
117 } catch (IllegalAccessException e) {\r
118 log.log(Level.SEVERE, e.getMessage(), e);\r
119 } catch (InvocationTargetException e) {\r
120 log.log(Level.SEVERE, e.getMessage(), e);\r
121 }\r
122 }\r
123 }\r
124\r
125 return attribute;\r
126 }\r
127\r
128 /**\r
129 * @param byteArray\r
130 * @param byteCount\r
131 * @param lastAttribute\r
132 * @return\r
133 */\r
134 private static Attribute parseAttribute(InputStream in, Attribute lastAttribute)\r
135 throws IOException, EndOfAttributesException {\r
136\r
137 int valueTag;\r
138 while ((valueTag = in.read()) < IppValueTag.UNSUPPORTED_VALUE.getValue()) {\r
139 if (valueTag == IppDelimiterTag.END_ATTRIBUTES.getValue()) {\r
140 throw END_OF_ATTRIBUTES_EXCEPTION;\r
141 }\r
142 }\r
143\r
144 int nameLength = parseInt2(in);\r
145 // parse the Attribute-Name\r
146 String name;\r
147 if (nameLength == 0) {\r
148 name = lastAttribute.getName();\r
149 } else {\r
150 name = parseString(in, nameLength);\r
151 }\r
152 \r
153\r
154 Object[] values = parseValues(valueTag, in);\r
155\r
156 if (name.equals(IppAttributeName.PRINTER_STATE_REASONS.getName())) {\r
157 return parsePrinterStateReasons((String) values[0], lastAttribute);\r
158 } else if (name.equals(IppAttributeName.JOB_STATE_REASONS.getName())) {\r
159 return parseJobStateReasons(values, lastAttribute);\r
160 } else {\r
161 return getAttribute(name, values);\r
162 }\r
163 }\r
164 \r
165 private static String parseString(InputStream in, int nameLength) throws IOException{\r
166 return parseString(in, nameLength, Charset.US_ASCII.getValue());\r
167 }\r
168 \r
169 private static String parseNameAndTextString(InputStream in, int nameLength) throws IOException{\r
170 return parseString(in, nameLength, AttributeWriter.DEFAULT_CHARSET.getValue());\r
171 }\r
172\r
173 /**\r
174 * @param in\r
175 * @param nameLength\r
176 */\r
177 private static String parseString(InputStream in, int nameLength, String charsetName) throws IOException {\r
178 byte[] bytes = new byte[nameLength];\r
179 in.read(bytes);\r
180 return new String(bytes, charsetName);\r
181\r
182 }\r
183 \r
184 \r
185 \r
186 \r
187 /**\r
188 * @param byteArray\r
189 * @param valueOffset\r
190 * @return\r
191 */\r
192 private static Date parseDate(InputStream in) throws IOException {\r
193 DecimalFormat twoDigits = new DecimalFormat("00");\r
194 DecimalFormat threeDigits = new DecimalFormat("000");\r
195 DecimalFormat fourDigits = new DecimalFormat("0000");\r
196 //year is encoded in network-byte order\r
197 int year = parseInt2(in);\r
198 int month = in.read();\r
199 int day = in.read();\r
200 int hour = in.read();\r
201 int minute = in.read();\r
202 int second = in.read();\r
203 int deci = in.read();\r
204 int mili = deci * 100;\r
205 char direction = (char) in.read();\r
206 int hoursFromUtc = (int) in.read();\r
207 int minutesFromUtc = (int) in.read();\r
208\r
209 String yearString = fourDigits.format((long) year);\r
210 String monthString = twoDigits.format((long) month);\r
211 String dayString = twoDigits.format((long) day);\r
212 String hourString = twoDigits.format((long) hour);\r
213 String minuteString = twoDigits.format((long) minute);\r
214 String secondString = twoDigits.format((long) second);\r
215 String miliString = threeDigits.format((long) mili);\r
216 String timeZone = direction + twoDigits.format((long) hoursFromUtc) + twoDigits.format((long) minutesFromUtc);\r
217 String dateString =\r
218 yearString\r
219 + "-"\r
220 + monthString\r
221 + "-"\r
222 + dayString\r
223 + " "\r
224 + hourString\r
225 + ":"\r
226 + minuteString\r
227 + ":"\r
228 + secondString\r
229 + ":"\r
230 + miliString\r
231 + timeZone;\r
232 DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSSZ");\r
233 Date date = null;\r
234 try {\r
235 date = dateFormat.parse(dateString);\r
236 } catch (ParseException e) {\r
237 log.log(Level.SEVERE, e.getMessage(), e);\r
238 }\r
239 return date;\r
240 }\r
241\r
242 private static int parseInt4(InputStream in) throws IOException {\r
243\r
244 //Same parsing as in java.io.DataInput readInt()\r
245 int a = in.read();\r
246 int b = in.read();\r
247 int c = in.read();\r
248 int d = in.read();\r
249 int value = (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff));\r
250 return value;\r
251 }\r
252\r
253 /**\r
254 * @param bytes\r
255 * @param offset\r
256 * @return\r
257 */\r
258 private static int parseInt4(byte[] bytes, int offset) {\r
259\r
260 //Same parsing as in java.io.DataInput readInt()\r
261 byte a = bytes[offset++];\r
262 byte b = bytes[offset++];\r
263 byte c = bytes[offset++];\r
264 byte d = bytes[offset++];\r
265 int value = (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff));\r
266 return value;\r
267 }\r
268\r
269 private static int parseInt2(InputStream in) throws IOException {\r
270\r
271 //Same parsing as in java.io.DataInput readInt()\r
272 int a = in.read();\r
273 int b = in.read();\r
274 int value = ((a & 0xff) << 8) | ((b & 0xff));\r
275 return value;\r
276 }\r
277\r
278 \r
279\r
280 /**\r
281 * @param string\r
282 * @param lastAttribute\r
283 * @return\r
284 */\r
285 private static Attribute parseJobStateReasons(Object[] values, Attribute lastAttribute) {\r
286 JobStateReasons reasons;\r
287 if (lastAttribute instanceof JobStateReasons) {\r
288 reasons = (JobStateReasons) lastAttribute;\r
289 } else {\r
290 reasons = new JobStateReasons();\r
291 }\r
292 JobStateReason reason = null;\r
293 if (values[0].equals(LdJobStateReason.NONE.toString())) {\r
294 reason = LdJobStateReason.NONE;\r
295 } else {\r
296 reason = (JobStateReason) getAttribute(IppAttributeName.JOB_STATE_REASON.getName(), values);\r
297 }\r
298 reasons.add(reason);\r
299 return reasons;\r
300 }\r
301\r
302 /**\r
303 * @param reasonAndSeverity\r
304 * @param lastAttribute\r
305 * @return\r
306 */\r
307 private static PrinterStateReasons parsePrinterStateReasons(String reasonAndSeverity, Attribute lastAttribute) {\r
308 Severity severity = null;\r
309 int severityOffset = 0;\r
310 if ((severityOffset = reasonAndSeverity.indexOf(Severity.ERROR.toString())) > 0) {\r
311 severity = Severity.ERROR;\r
312 } else if ((severityOffset = reasonAndSeverity.indexOf(Severity.REPORT.toString())) > 0) {\r
313 severity = Severity.REPORT;\r
314 } else if ((severityOffset = reasonAndSeverity.indexOf(Severity.WARNING.toString())) > 0) {\r
315 severity = Severity.WARNING;\r
316 }\r
317 String reasonString;\r
318 if (severityOffset != -1) {\r
319 //subtract 1 for the hyphen\r
320 severityOffset--;\r
321 reasonString = reasonAndSeverity.substring(0, severityOffset - 1);\r
322 } else {\r
323 reasonString = reasonAndSeverity;\r
324 }\r
325 Object[] values = new Object[] { reasonString };\r
326 PrinterStateReason reason =\r
327 (PrinterStateReason) getAttribute(IppAttributeName.PRINTER_STATE_REASON.getName(), values);\r
328 PrinterStateReasons reasons;\r
329 if (lastAttribute instanceof PrinterStateReasons) {\r
330 reasons = (PrinterStateReasons) lastAttribute;\r
331 } else {\r
332 reasons = new PrinterStateReasons();\r
333 }\r
334 if (reason != null) {\r
335 if (severity == null) {\r
336 severity = Severity.ERROR;\r
337 }\r
338 reasons.put(reason, severity);\r
339 }\r
340 return reasons;\r
341 }\r
342\r
343 /**\r
344 * @param bytes\r
345 * @return map of attributes (key -> category, value -> Set with attributes)\r
346 * \r
347 *\r
348 */\r
349 public static Map parseResponse(InputStream response) throws IOException {\r
350 Map attributes = new HashMap();\r
351 long start = System.currentTimeMillis();\r
352 Attribute lastAttribute = null;\r
353 boolean finished = false;\r
354 response.read();\r
355\r
356 while (!finished) {\r
357 Attribute attribute = null;\r
358 try {\r
359 attribute = parseAttribute(response, lastAttribute);\r
360 if (attribute != null) {\r
361 lastAttribute = attribute;\r
362 attributes = put(attributes, attribute);\r
363 if (log.isLoggable(Level.FINEST)) {\r
364 log.finest("parsed attribute(" + attribute.getName() + "): " + attribute.toString());\r
365 }\r
366 } else {\r
367 if (log.isLoggable(Level.FINEST)) {\r
368\r
369 log.finest("Attribute was null");\r
370 }\r
371 }\r
372 } catch (EndOfAttributesException e) {\r
373\r
374 finished = true;\r
375 if (log.isLoggable(Level.INFO)) {\r
376 log.info("--- Attribute parsing finished ---");\r
377 }\r
378 }\r
379 }\r
380 long end = System.currentTimeMillis();\r
381 if (log.isLoggable(Level.INFO)) {\r
382 log.info("Parsing took " + (end - start) + "ms.");\r
383 }\r
384 return attributes;\r
385 }\r
386\r
387 \r
388 /**\r
389 * @param byteArray\r
390 * @param valueOffset\r
391 * @param valueLength\r
392 * @return\r
393 */\r
394 private static URI parseUri(InputStream in, int valueLength) throws IOException {\r
395 String uriString = parseString(in, valueLength);\r
396 URI uri = null;\r
397 try {\r
398 uri = new URI(uriString);\r
399 } catch (URISyntaxException e) {\r
400 throw new RuntimeException(e);\r
401 }\r
402 return uri;\r
403 }\r
404\r
405 \r
406 /**\r
407 * @param valueTag\r
408 * @param byteArray\r
409 * @param valueOffset\r
410 * @param valueLength\r
411 * @return\r
412 */\r
413 private static Object[] parseValues(int valueTag, InputStream in) throws IOException {\r
414 int valueLength = parseInt2(in);\r
415 Object[] values = null;\r
416 if (valueTag == IppValueTag.INTEGER.getValue() || valueTag == IppValueTag.ENUM.getValue()) {\r
417 Integer number = new Integer(parseInt4(in));\r
418 values = new Object[] { number };\r
419 } else if (\r
420 valueTag == IppValueTag.STRING.getValue()\r
421 || valueTag == IppValueTag.TEXT.getValue()\r
422 || valueTag == IppValueTag.NAME.getValue()){\r
423 String word = parseNameAndTextString(in, valueLength);\r
424 values = new Object[] { word, Locale.getDefault()}; \r
425 } else if (\r
426 valueTag == IppValueTag.CHARSET.getValue()\r
427 || valueTag == IppValueTag.LANGUAGE.getValue()\r
428 || valueTag == IppValueTag.MIMETYPE.getValue()) {\r
429 String word = parseString(in, valueLength);\r
430 values = new Object[] { word, Locale.getDefault()};\r
431 } else if (valueTag == IppValueTag.URI.getValue()) {\r
432 URI uri = parseUri(in, valueLength);\r
433 values = new Object[] { uri };\r
434 } else if (valueTag == IppValueTag.KEYWORD.getValue()) {\r
435 String word = parseString(in, valueLength);\r
436 values = new Object[] { word, Locale.getDefault()};\r
437 } else if (valueTag == IppValueTag.BOOLEAN.getValue()) {\r
438 Integer bool = new Integer(in.read());\r
439 values = new Object[] { bool };\r
440 } else if (valueTag == IppValueTag.RANGE.getValue()) {\r
441 Integer lowerBound = new Integer(parseInt4(in));\r
442 Integer upperBound = new Integer(parseInt4(in));\r
443 values = new Object[] { lowerBound, upperBound };\r
444 } else if (valueTag == IppValueTag.DATE.getValue()) {\r
445\r
446 Date date = parseDate(in);\r
447 values = new Object[] { date };\r
448 } else if (valueTag == IppValueTag.NOVALUE.getValue()) {\r
449 values = new Object[] {\r
450 };\r
451 } else {\r
452 throw new IllegalArgumentException("\"" + Integer.toHexString(valueTag) + "\" is not a valid value-tag");\r
453 }\r
454 return values;\r
455 }\r
456\r
457\r
458 /**\r
459 * @param attributes\r
460 * @param attribute\r
461 */\r
462 private static Map put(Map attributes, Attribute attribute) {\r
463 Set values = (Set) attributes.get(attribute.getCategory());\r
464 if (values == null) {\r
465 values = new HashSet();\r
466 attributes.put(attribute.getCategory(), values);\r
467 }\r
468 values.add(attribute);\r
469 return attributes;\r
470 }\r
471\r
472 /**\r
473 * @param values\r
474 * @return\r
475 */\r
476 private static Class[] toClassArray(Object[] values) {\r
477 Class[] classes = new Class[values.length];\r
478 for (int i = 0; i < values.length; i++) {\r
479 Class clazz = values[i].getClass();\r
480 if (clazz.equals(Integer.class)) {\r
481 clazz = int.class;\r
482 }\r
483 classes[i] = clazz;\r
484 }\r
485 return classes;\r
486 }\r
487\r
488}\r