+package com.google.code.jspi;\r
+\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.lang.reflect.Constructor;\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+import java.text.DateFormat;\r
+import java.text.DecimalFormat;\r
+import java.text.ParseException;\r
+import java.text.SimpleDateFormat;\r
+import java.util.Date;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.Locale;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+import javax.print.attribute.Attribute;\r
+import javax.print.attribute.EnumSyntax;\r
+\r
+import de.lohndirekt.print.attribute.AttributeWriter;\r
+import de.lohndirekt.print.attribute.IppAttributeName;\r
+import de.lohndirekt.print.attribute.IppDelimiterTag;\r
+import de.lohndirekt.print.attribute.IppValueTag;\r
+import de.lohndirekt.print.attribute.ipp.Charset;\r
+import de.lohndirekt.print.exception.EndOfAttributesException;\r
+\r
+/**\r
+ * @author bpusch\r
+ *\r
+ */\r
+public final class AttributeParser {\r
+\r
+ private final static EndOfAttributesException END_OF_ATTRIBUTES_EXCEPTION = new EndOfAttributesException();\r
+\r
+ private final static Logger log = Logger.getLogger(AttributeParser.class.getName());\r
+\r
+ \r
+\r
+ /**\r
+ * @param name\r
+ * @param values\r
+ * @return\r
+ */\r
+ private static Attribute getAttribute(String name, Object[] values) {\r
+ Attribute attribute = null;\r
+ IppAttributeName attrName = IppAttributeName.get(name);\r
+ Class attrClass = attrName.getAttributeClass();\r
+ Class superClass = attrClass.getSuperclass();\r
+ if (superClass != null) {\r
+ if (superClass.equals(EnumSyntax.class)) {\r
+ try {\r
+ Field[] fields = attrClass.getDeclaredFields();\r
+ for (int i = 0; i < fields.length; i++) {\r
+ Field field = fields[i];\r
+ if (field.getType().equals(attrClass)) {\r
+ EnumSyntax attr = (EnumSyntax) field.get(null);\r
+ if (values[0] instanceof String) {\r
+ if (attr.toString().equals(values[0])) {\r
+ attribute = (Attribute) attr;\r
+ break;\r
+ }\r
+ } else {\r
+ if (attr.getValue() == ((Integer) values[0]).intValue()) {\r
+ attribute = (Attribute) attr;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ } catch (SecurityException e) {\r
+ log.log(Level.SEVERE, e.getMessage(), e);\r
+ } catch (IllegalArgumentException e) {\r
+ log.log(Level.SEVERE, e.getMessage(), e);\r
+ } catch (IllegalAccessException e) {\r
+ log.log(Level.SEVERE, e.getMessage(), e);\r
+ }\r
+\r
+ } else {\r
+ Class[] parameters = toClassArray(values);\r
+ try {\r
+ Constructor constructor = attrClass.getDeclaredConstructor(parameters);\r
+ attribute = (Attribute) constructor.newInstance(values);\r
+ } catch (SecurityException e) {\r
+ log.log(Level.SEVERE, e.getMessage(), e);\r
+ } catch (NoSuchMethodException e) {\r
+ log.log(Level.SEVERE, e.getMessage(), e);\r
+ } catch (IllegalArgumentException e) {\r
+ log.log(Level.SEVERE, e.getMessage(), e);\r
+ } catch (InstantiationException e) {\r
+ log.log(Level.SEVERE, e.getMessage(), e);\r
+ } catch (IllegalAccessException e) {\r
+ log.log(Level.SEVERE, e.getMessage(), e);\r
+ } catch (InvocationTargetException e) {\r
+ log.log(Level.SEVERE, e.getMessage(), e);\r
+ }\r
+ }\r
+ }\r
+\r
+ return attribute;\r
+ }\r
+\r
+ /**\r
+ * @param byteArray\r
+ * @param byteCount\r
+ * @param lastAttribute\r
+ * @return\r
+ */\r
+ private static Attribute parseAttribute(InputStream in, Attribute lastAttribute)\r
+ throws IOException, EndOfAttributesException {\r
+\r
+ int valueTag;\r
+ while ((valueTag = in.read()) < IppValueTag.UNSUPPORTED_VALUE.getValue()) {\r
+ if (valueTag == IppDelimiterTag.END_ATTRIBUTES.getValue()) {\r
+ throw END_OF_ATTRIBUTES_EXCEPTION;\r
+ }\r
+ }\r
+\r
+ int nameLength = parseInt2(in);\r
+ // parse the Attribute-Name\r
+ String name;\r
+ if (nameLength == 0) {\r
+ name = lastAttribute.getName();\r
+ } else {\r
+ name = parseString(in, nameLength);\r
+ }\r
+ \r
+\r
+ Object[] values = parseValues(valueTag, in);\r
+\r
+ return getAttribute(name, values);\r
+ }\r
+ \r
+ private static String parseString(InputStream in, int nameLength) throws IOException{\r
+ return parseString(in, nameLength, Charset.US_ASCII.getValue());\r
+ }\r
+ \r
+ private static String parseNameAndTextString(InputStream in, int nameLength) throws IOException{\r
+ return parseString(in, nameLength, AttributeWriter.DEFAULT_CHARSET.getValue());\r
+ }\r
+\r
+ /**\r
+ * @param in\r
+ * @param nameLength\r
+ */\r
+ private static String parseString(InputStream in, int nameLength, String charsetName) throws IOException {\r
+ byte[] bytes = new byte[nameLength];\r
+ in.read(bytes);\r
+ return new String(bytes, charsetName);\r
+\r
+ }\r
+ \r
+ \r
+ \r
+ \r
+ /**\r
+ * @param byteArray\r
+ * @param valueOffset\r
+ * @return\r
+ */\r
+ private static Date parseDate(InputStream in) throws IOException {\r
+ DecimalFormat twoDigits = new DecimalFormat("00");\r
+ DecimalFormat threeDigits = new DecimalFormat("000");\r
+ DecimalFormat fourDigits = new DecimalFormat("0000");\r
+ //year is encoded in network-byte order\r
+ int year = parseInt2(in);\r
+ int month = in.read();\r
+ int day = in.read();\r
+ int hour = in.read();\r
+ int minute = in.read();\r
+ int second = in.read();\r
+ int deci = in.read();\r
+ int mili = deci * 100;\r
+ char direction = (char) in.read();\r
+ int hoursFromUtc = in.read();\r
+ int minutesFromUtc = in.read();\r
+\r
+ String yearString = fourDigits.format(year);\r
+ String monthString = twoDigits.format(month);\r
+ String dayString = twoDigits.format(day);\r
+ String hourString = twoDigits.format(hour);\r
+ String minuteString = twoDigits.format(minute);\r
+ String secondString = twoDigits.format(second);\r
+ String miliString = threeDigits.format(mili);\r
+ String timeZone = direction + twoDigits.format(hoursFromUtc) + twoDigits.format(minutesFromUtc);\r
+ String dateString =\r
+ yearString\r
+ + "-"\r
+ + monthString\r
+ + "-"\r
+ + dayString\r
+ + " "\r
+ + hourString\r
+ + ":"\r
+ + minuteString\r
+ + ":"\r
+ + secondString\r
+ + ":"\r
+ + miliString\r
+ + timeZone;\r
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSSZ");\r
+ Date date = null;\r
+ try {\r
+ date = dateFormat.parse(dateString);\r
+ } catch (ParseException e) {\r
+ log.log(Level.SEVERE, e.getMessage(), e);\r
+ }\r
+ return date;\r
+ }\r
+\r
+ private static int parseInt4(InputStream in) throws IOException {\r
+\r
+ //Same parsing as in java.io.DataInput readInt()\r
+ int a = in.read();\r
+ int b = in.read();\r
+ int c = in.read();\r
+ int d = in.read();\r
+ int value = (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff));\r
+ return value;\r
+ }\r
+\r
+ /**\r
+ * @param bytes\r
+ * @param offset\r
+ * @return\r
+ */\r
+ private static int parseInt4(byte[] bytes, int offset) {\r
+\r
+ //Same parsing as in java.io.DataInput readInt()\r
+ byte a = bytes[offset++];\r
+ byte b = bytes[offset++];\r
+ byte c = bytes[offset++];\r
+ byte d = bytes[offset++];\r
+ int value = (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff));\r
+ return value;\r
+ }\r
+\r
+ private static int parseInt2(InputStream in) throws IOException {\r
+\r
+ //Same parsing as in java.io.DataInput readInt()\r
+ int a = in.read();\r
+ int b = in.read();\r
+ int value = ((a & 0xff) << 8) | ((b & 0xff));\r
+ return value;\r
+ }\r
+\r
+ /**\r
+ * @param bytes\r
+ * @return map of attributes (key -> category, value -> Set with attributes)\r
+ * \r
+ *\r
+ */\r
+ public static Map parseRequest(InputStream response) throws IOException {\r
+ Map attributes = new HashMap();\r
+ long start = System.currentTimeMillis();\r
+ Attribute lastAttribute = null;\r
+ boolean finished = false;\r
+ response.read();\r
+\r
+ while (!finished) {\r
+ Attribute attribute = null;\r
+ try {\r
+ attribute = parseAttribute(response, lastAttribute);\r
+ if (attribute != null) {\r
+ lastAttribute = attribute;\r
+ attributes = put(attributes, attribute);\r
+ if (log.isLoggable(Level.FINEST)) {\r
+ log.finest("parsed attribute(" + attribute.getName() + "): " + attribute.toString());\r
+ }\r
+ } else {\r
+ if (log.isLoggable(Level.FINEST)) {\r
+\r
+ log.finest("Attribute was null");\r
+ }\r
+ }\r
+ } catch (EndOfAttributesException e) {\r
+\r
+ finished = true;\r
+ if (log.isLoggable(Level.INFO)) {\r
+ log.info("--- Attribute parsing finished ---");\r
+ }\r
+ }\r
+ }\r
+ long end = System.currentTimeMillis();\r
+ if (log.isLoggable(Level.INFO)) {\r
+ log.info("Parsing took " + (end - start) + "ms.");\r
+ }\r
+ return attributes;\r
+ }\r
+\r
+ \r
+ /**\r
+ * @param byteArray\r
+ * @param valueOffset\r
+ * @param valueLength\r
+ * @return\r
+ */\r
+ private static URI parseUri(InputStream in, int valueLength) throws IOException {\r
+ String uriString = parseString(in, valueLength);\r
+ URI uri = null;\r
+ try {\r
+ uri = new URI(uriString);\r
+ } catch (URISyntaxException e) {\r
+ throw new RuntimeException(e);\r
+ }\r
+ return uri;\r
+ }\r
+\r
+ \r
+ /**\r
+ * @param valueTag\r
+ * @param byteArray\r
+ * @param valueOffset\r
+ * @param valueLength\r
+ * @return\r
+ */\r
+ private static Object[] parseValues(int valueTag, InputStream in) throws IOException {\r
+ int valueLength = parseInt2(in);\r
+ Object[] values = null;\r
+ if (valueTag == IppValueTag.INTEGER.getValue() || valueTag == IppValueTag.ENUM.getValue()) {\r
+ Integer number = new Integer(parseInt4(in));\r
+ values = new Object[] { number };\r
+ } else if (\r
+ valueTag == IppValueTag.STRING.getValue()\r
+ || valueTag == IppValueTag.TEXT.getValue()\r
+ || valueTag == IppValueTag.NAME.getValue()){\r
+ String word = parseNameAndTextString(in, valueLength);\r
+ values = new Object[] { word, Locale.getDefault()}; \r
+ } else if (\r
+ valueTag == IppValueTag.CHARSET.getValue()\r
+ || valueTag == IppValueTag.LANGUAGE.getValue()\r
+ || valueTag == IppValueTag.MIMETYPE.getValue()) {\r
+ String word = parseString(in, valueLength);\r
+ values = new Object[] { word, Locale.getDefault()};\r
+ } else if (valueTag == IppValueTag.URI.getValue()) {\r
+ URI uri = parseUri(in, valueLength);\r
+ values = new Object[] { uri };\r
+ } else if (valueTag == IppValueTag.KEYWORD.getValue()) {\r
+ String word = parseString(in, valueLength);\r
+ values = new Object[] { word, Locale.getDefault()};\r
+ } else if (valueTag == IppValueTag.BOOLEAN.getValue()) {\r
+ Integer bool = new Integer(in.read());\r
+ values = new Object[] { bool };\r
+ } else if (valueTag == IppValueTag.RANGE.getValue()) {\r
+ Integer lowerBound = new Integer(parseInt4(in));\r
+ Integer upperBound = new Integer(parseInt4(in));\r
+ values = new Object[] { lowerBound, upperBound };\r
+ } else if (valueTag == IppValueTag.DATE.getValue()) {\r
+\r
+ Date date = parseDate(in);\r
+ values = new Object[] { date };\r
+ } else if (valueTag == IppValueTag.NOVALUE.getValue()) {\r
+ values = new Object[] {\r
+ };\r
+ } else {\r
+ throw new IllegalArgumentException("\"" + Integer.toHexString(valueTag) + "\" is not a valid value-tag");\r
+ }\r
+ return values;\r
+ }\r
+\r
+\r
+ /**\r
+ * @param attributes\r
+ * @param attribute\r
+ */\r
+ private static Map put(Map attributes, Attribute attribute) {\r
+ Set values = (Set) attributes.get(attribute.getCategory());\r
+ if (values == null) {\r
+ values = new HashSet();\r
+ attributes.put(attribute.getCategory(), values);\r
+ }\r
+ values.add(attribute);\r
+ return attributes;\r
+ }\r
+\r
+ /**\r
+ * @param values\r
+ * @return\r
+ */\r
+ private static Class[] toClassArray(Object[] values) {\r
+ Class[] classes = new Class[values.length];\r
+ for (int i = 0; i < values.length; i++) {\r
+ Class clazz = values[i].getClass();\r
+ if (clazz.equals(Integer.class)) {\r
+ clazz = int.class;\r
+ }\r
+ classes[i] = clazz;\r
+ }\r
+ return classes;\r
+ }\r
+\r
+}\r