Initial import.
[clinton/mirror/jspi/.git] / jspi / src / main / java / com / google / code / jspi / AttributeParser.java
1 package com.google.code.jspi;
2
3
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.lang.reflect.Constructor;
7 import java.lang.reflect.Field;
8 import java.lang.reflect.InvocationTargetException;
9 import java.net.URI;
10 import java.net.URISyntaxException;
11 import java.text.DateFormat;
12 import java.text.DecimalFormat;
13 import java.text.ParseException;
14 import java.text.SimpleDateFormat;
15 import java.util.Date;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.Locale;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.logging.Level;
22 import java.util.logging.Logger;
23
24 import javax.print.attribute.Attribute;
25 import javax.print.attribute.EnumSyntax;
26
27 import de.lohndirekt.print.attribute.AttributeWriter;
28 import de.lohndirekt.print.attribute.IppAttributeName;
29 import de.lohndirekt.print.attribute.IppDelimiterTag;
30 import de.lohndirekt.print.attribute.IppValueTag;
31 import de.lohndirekt.print.attribute.ipp.Charset;
32 import de.lohndirekt.print.exception.EndOfAttributesException;
33
34 /**
35 * @author bpusch
36 *
37 */
38 public final class AttributeParser {
39
40 private final static EndOfAttributesException END_OF_ATTRIBUTES_EXCEPTION = new EndOfAttributesException();
41
42 private final static Logger log = Logger.getLogger(AttributeParser.class.getName());
43
44
45
46 /**
47 * @param name
48 * @param values
49 * @return
50 */
51 private static Attribute getAttribute(String name, Object[] values) {
52 Attribute attribute = null;
53 IppAttributeName attrName = IppAttributeName.get(name);
54 Class attrClass = attrName.getAttributeClass();
55 Class superClass = attrClass.getSuperclass();
56 if (superClass != null) {
57 if (superClass.equals(EnumSyntax.class)) {
58 try {
59 Field[] fields = attrClass.getDeclaredFields();
60 for (int i = 0; i < fields.length; i++) {
61 Field field = fields[i];
62 if (field.getType().equals(attrClass)) {
63 EnumSyntax attr = (EnumSyntax) field.get(null);
64 if (values[0] instanceof String) {
65 if (attr.toString().equals(values[0])) {
66 attribute = (Attribute) attr;
67 break;
68 }
69 } else {
70 if (attr.getValue() == ((Integer) values[0]).intValue()) {
71 attribute = (Attribute) attr;
72 break;
73 }
74 }
75 }
76 }
77 } catch (SecurityException e) {
78 log.log(Level.SEVERE, e.getMessage(), e);
79 } catch (IllegalArgumentException e) {
80 log.log(Level.SEVERE, e.getMessage(), e);
81 } catch (IllegalAccessException e) {
82 log.log(Level.SEVERE, e.getMessage(), e);
83 }
84
85 } else {
86 Class[] parameters = toClassArray(values);
87 try {
88 Constructor constructor = attrClass.getDeclaredConstructor(parameters);
89 attribute = (Attribute) constructor.newInstance(values);
90 } catch (SecurityException e) {
91 log.log(Level.SEVERE, e.getMessage(), e);
92 } catch (NoSuchMethodException e) {
93 log.log(Level.SEVERE, e.getMessage(), e);
94 } catch (IllegalArgumentException e) {
95 log.log(Level.SEVERE, e.getMessage(), e);
96 } catch (InstantiationException e) {
97 log.log(Level.SEVERE, e.getMessage(), e);
98 } catch (IllegalAccessException e) {
99 log.log(Level.SEVERE, e.getMessage(), e);
100 } catch (InvocationTargetException e) {
101 log.log(Level.SEVERE, e.getMessage(), e);
102 }
103 }
104 }
105
106 return attribute;
107 }
108
109 /**
110 * @param byteArray
111 * @param byteCount
112 * @param lastAttribute
113 * @return
114 */
115 private static Attribute parseAttribute(InputStream in, Attribute lastAttribute)
116 throws IOException, EndOfAttributesException {
117
118 int valueTag;
119 while ((valueTag = in.read()) < IppValueTag.UNSUPPORTED_VALUE.getValue()) {
120 if (valueTag == IppDelimiterTag.END_ATTRIBUTES.getValue()) {
121 throw END_OF_ATTRIBUTES_EXCEPTION;
122 }
123 }
124
125 int nameLength = parseInt2(in);
126 // parse the Attribute-Name
127 String name;
128 if (nameLength == 0) {
129 name = lastAttribute.getName();
130 } else {
131 name = parseString(in, nameLength);
132 }
133
134
135 Object[] values = parseValues(valueTag, in);
136
137 return getAttribute(name, values);
138 }
139
140 private static String parseString(InputStream in, int nameLength) throws IOException{
141 return parseString(in, nameLength, Charset.US_ASCII.getValue());
142 }
143
144 private static String parseNameAndTextString(InputStream in, int nameLength) throws IOException{
145 return parseString(in, nameLength, AttributeWriter.DEFAULT_CHARSET.getValue());
146 }
147
148 /**
149 * @param in
150 * @param nameLength
151 */
152 private static String parseString(InputStream in, int nameLength, String charsetName) throws IOException {
153 byte[] bytes = new byte[nameLength];
154 in.read(bytes);
155 return new String(bytes, charsetName);
156
157 }
158
159
160
161
162 /**
163 * @param byteArray
164 * @param valueOffset
165 * @return
166 */
167 private static Date parseDate(InputStream in) throws IOException {
168 DecimalFormat twoDigits = new DecimalFormat("00");
169 DecimalFormat threeDigits = new DecimalFormat("000");
170 DecimalFormat fourDigits = new DecimalFormat("0000");
171 //year is encoded in network-byte order
172 int year = parseInt2(in);
173 int month = in.read();
174 int day = in.read();
175 int hour = in.read();
176 int minute = in.read();
177 int second = in.read();
178 int deci = in.read();
179 int mili = deci * 100;
180 char direction = (char) in.read();
181 int hoursFromUtc = in.read();
182 int minutesFromUtc = in.read();
183
184 String yearString = fourDigits.format(year);
185 String monthString = twoDigits.format(month);
186 String dayString = twoDigits.format(day);
187 String hourString = twoDigits.format(hour);
188 String minuteString = twoDigits.format(minute);
189 String secondString = twoDigits.format(second);
190 String miliString = threeDigits.format(mili);
191 String timeZone = direction + twoDigits.format(hoursFromUtc) + twoDigits.format(minutesFromUtc);
192 String dateString =
193 yearString
194 + "-"
195 + monthString
196 + "-"
197 + dayString
198 + " "
199 + hourString
200 + ":"
201 + minuteString
202 + ":"
203 + secondString
204 + ":"
205 + miliString
206 + timeZone;
207 DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSSZ");
208 Date date = null;
209 try {
210 date = dateFormat.parse(dateString);
211 } catch (ParseException e) {
212 log.log(Level.SEVERE, e.getMessage(), e);
213 }
214 return date;
215 }
216
217 private static int parseInt4(InputStream in) throws IOException {
218
219 //Same parsing as in java.io.DataInput readInt()
220 int a = in.read();
221 int b = in.read();
222 int c = in.read();
223 int d = in.read();
224 int value = (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff));
225 return value;
226 }
227
228 /**
229 * @param bytes
230 * @param offset
231 * @return
232 */
233 private static int parseInt4(byte[] bytes, int offset) {
234
235 //Same parsing as in java.io.DataInput readInt()
236 byte a = bytes[offset++];
237 byte b = bytes[offset++];
238 byte c = bytes[offset++];
239 byte d = bytes[offset++];
240 int value = (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff));
241 return value;
242 }
243
244 private static int parseInt2(InputStream in) throws IOException {
245
246 //Same parsing as in java.io.DataInput readInt()
247 int a = in.read();
248 int b = in.read();
249 int value = ((a & 0xff) << 8) | ((b & 0xff));
250 return value;
251 }
252
253 /**
254 * @param bytes
255 * @return map of attributes (key -> category, value -> Set with attributes)
256 *
257 *
258 */
259 public static Map parseRequest(InputStream response) throws IOException {
260 Map attributes = new HashMap();
261 long start = System.currentTimeMillis();
262 Attribute lastAttribute = null;
263 boolean finished = false;
264 response.read();
265
266 while (!finished) {
267 Attribute attribute = null;
268 try {
269 attribute = parseAttribute(response, lastAttribute);
270 if (attribute != null) {
271 lastAttribute = attribute;
272 attributes = put(attributes, attribute);
273 if (log.isLoggable(Level.FINEST)) {
274 log.finest("parsed attribute(" + attribute.getName() + "): " + attribute.toString());
275 }
276 } else {
277 if (log.isLoggable(Level.FINEST)) {
278
279 log.finest("Attribute was null");
280 }
281 }
282 } catch (EndOfAttributesException e) {
283
284 finished = true;
285 if (log.isLoggable(Level.INFO)) {
286 log.info("--- Attribute parsing finished ---");
287 }
288 }
289 }
290 long end = System.currentTimeMillis();
291 if (log.isLoggable(Level.INFO)) {
292 log.info("Parsing took " + (end - start) + "ms.");
293 }
294 return attributes;
295 }
296
297
298 /**
299 * @param byteArray
300 * @param valueOffset
301 * @param valueLength
302 * @return
303 */
304 private static URI parseUri(InputStream in, int valueLength) throws IOException {
305 String uriString = parseString(in, valueLength);
306 URI uri = null;
307 try {
308 uri = new URI(uriString);
309 } catch (URISyntaxException e) {
310 throw new RuntimeException(e);
311 }
312 return uri;
313 }
314
315
316 /**
317 * @param valueTag
318 * @param byteArray
319 * @param valueOffset
320 * @param valueLength
321 * @return
322 */
323 private static Object[] parseValues(int valueTag, InputStream in) throws IOException {
324 int valueLength = parseInt2(in);
325 Object[] values = null;
326 if (valueTag == IppValueTag.INTEGER.getValue() || valueTag == IppValueTag.ENUM.getValue()) {
327 Integer number = new Integer(parseInt4(in));
328 values = new Object[] { number };
329 } else if (
330 valueTag == IppValueTag.STRING.getValue()
331 || valueTag == IppValueTag.TEXT.getValue()
332 || valueTag == IppValueTag.NAME.getValue()){
333 String word = parseNameAndTextString(in, valueLength);
334 values = new Object[] { word, Locale.getDefault()};
335 } else if (
336 valueTag == IppValueTag.CHARSET.getValue()
337 || valueTag == IppValueTag.LANGUAGE.getValue()
338 || valueTag == IppValueTag.MIMETYPE.getValue()) {
339 String word = parseString(in, valueLength);
340 values = new Object[] { word, Locale.getDefault()};
341 } else if (valueTag == IppValueTag.URI.getValue()) {
342 URI uri = parseUri(in, valueLength);
343 values = new Object[] { uri };
344 } else if (valueTag == IppValueTag.KEYWORD.getValue()) {
345 String word = parseString(in, valueLength);
346 values = new Object[] { word, Locale.getDefault()};
347 } else if (valueTag == IppValueTag.BOOLEAN.getValue()) {
348 Integer bool = new Integer(in.read());
349 values = new Object[] { bool };
350 } else if (valueTag == IppValueTag.RANGE.getValue()) {
351 Integer lowerBound = new Integer(parseInt4(in));
352 Integer upperBound = new Integer(parseInt4(in));
353 values = new Object[] { lowerBound, upperBound };
354 } else if (valueTag == IppValueTag.DATE.getValue()) {
355
356 Date date = parseDate(in);
357 values = new Object[] { date };
358 } else if (valueTag == IppValueTag.NOVALUE.getValue()) {
359 values = new Object[] {
360 };
361 } else {
362 throw new IllegalArgumentException("\"" + Integer.toHexString(valueTag) + "\" is not a valid value-tag");
363 }
364 return values;
365 }
366
367
368 /**
369 * @param attributes
370 * @param attribute
371 */
372 private static Map put(Map attributes, Attribute attribute) {
373 Set values = (Set) attributes.get(attribute.getCategory());
374 if (values == null) {
375 values = new HashSet();
376 attributes.put(attribute.getCategory(), values);
377 }
378 values.add(attribute);
379 return attributes;
380 }
381
382 /**
383 * @param values
384 * @return
385 */
386 private static Class[] toClassArray(Object[] values) {
387 Class[] classes = new Class[values.length];
388 for (int i = 0; i < values.length; i++) {
389 Class clazz = values[i].getClass();
390 if (clazz.equals(Integer.class)) {
391 clazz = int.class;
392 }
393 classes[i] = clazz;
394 }
395 return classes;
396 }
397
398 }