Big refactoring of ParsedResult: now split into ResultParser and ParsedResult classes...
[zxing.git] / core / src / com / google / zxing / client / result / ResultParser.java
1 /*
2  * Copyright 2007 ZXing authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.google.zxing.client.result;
18
19 import com.google.zxing.Result;
20
21 import java.util.Hashtable;
22
23 /**
24  * <p>Abstract class representing the result of decoding a barcode, as more than
25  * a String -- as some type of structured data. This might be a subclass which represents
26  * a URL, or an e-mail address. {@link #parseReaderResult(com.google.zxing.Result)} will turn a raw
27  * decoded string into the most appropriate type of structured representation.</p>
28  *
29  * <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
30  * on exception-based mechanisms during parsing.</p>
31  *
32  * @author srowen@google.com (Sean Owen)
33  */
34 public abstract class ResultParser {
35
36   public static ParsedResult parseReaderResult(Result theResult) {
37     // This is a bit messy, but given limited options in MIDP / CLDC, this may well be the simplest
38     // way to go about this. For example, we have no reflection available, really.
39     // Order is important here.
40     ParsedResult result;
41     if ((result = BookmarkDoCoMoResultParser.parse(theResult)) != null) {
42       return result;
43     } else if ((result = AddressBookDoCoMoResultParser.parse(theResult)) != null) {
44       return result;
45     } else if ((result = EmailDoCoMoResultParser.parse(theResult)) != null) {
46       return result;
47     } else if ((result = EmailAddressResultParser.parse(theResult)) != null) {
48       return result;
49     } else if ((result = AddressBookAUResultParser.parse(theResult)) != null) {
50       return result;
51     } else if ((result = TelResultParser.parse(theResult)) != null) {
52       return result;
53     } else if ((result = SMSResultParser.parse(theResult)) != null) {
54       return result;
55     } else if ((result = SMSTOResultParser.parse(theResult)) != null) {
56       return result;
57     } else if ((result = GeoResultParser.parse(theResult)) != null) {
58       return result;
59     } else if ((result = URLTOResultParser.parse(theResult)) != null) {
60       return result;
61     } else if ((result = URIResultParser.parse(theResult)) != null) {
62       return result;
63     } else if ((result = UPCResultParser.parse(theResult)) != null) {
64       return result;
65     }
66     return new TextParsedResult(theResult.getText(), null);
67   }
68
69   protected static void maybeAppend(String value, StringBuffer result) {
70     if (value != null) {
71       result.append('\n');
72       result.append(value);
73     }
74   }
75
76   protected static void maybeAppend(String[] value, StringBuffer result) {
77     if (value != null) {
78       for (int i = 0; i < value.length; i++) {
79         result.append('\n');
80         result.append(value[i]);
81       }
82     }
83   }
84
85   protected static String unescapeBackslash(String escaped) {
86     if (escaped != null) {
87       int backslash = escaped.indexOf((int) '\\');
88       if (backslash >= 0) {
89         int max = escaped.length();
90         StringBuffer unescaped = new StringBuffer(max - 1);
91         unescaped.append(escaped.toCharArray(), 0, backslash);
92         boolean nextIsEscaped = false;
93         for (int i = backslash; i < max; i++) {
94           char c = escaped.charAt(i);
95           if (nextIsEscaped || c != '\\') {
96             unescaped.append(c);
97             nextIsEscaped = false;
98           } else {
99             nextIsEscaped = true;
100           }
101         }
102         return unescaped.toString();
103       }
104     }
105     return escaped;
106   }
107
108   protected static String urlDecode(String escaped) {
109
110     // No we can't use java.net.URLDecoder here. JavaME doesn't have it.
111     if (escaped == null) {
112       return null;
113     }
114     char[] escapedArray = escaped.toCharArray();
115
116     int first = findFirstEscape(escapedArray);
117     if (first < 0) {
118       return escaped;
119     }
120
121     int max = escapedArray.length;
122     // final length is at most 2 less than original due to at least 1 unescaping
123     StringBuffer unescaped = new StringBuffer(max - 2);
124     // Can append everything up to first escape character
125     unescaped.append(escapedArray, 0, first);
126
127     for (int i = first; i < max; i++) {
128       char c = escapedArray[i];
129       if (c == '+') {
130         // + is translated directly into a space
131         unescaped.append(' ');
132       } else if (c == '%') {
133         // Are there even two more chars? if not we will just copy the escaped sequence and be done
134         if (i >= max - 2) {
135           unescaped.append('%'); // append that % and move on
136         } else {
137           int firstDigitValue = parseHexDigit(escapedArray[++i]);
138           int secondDigitValue = parseHexDigit(escapedArray[++i]);
139           if (firstDigitValue < 0 || secondDigitValue < 0) {
140             // bad digit, just move on
141             unescaped.append('%');
142             unescaped.append(escapedArray[i-1]);
143             unescaped.append(escapedArray[i]);
144           }
145           unescaped.append((char) ((firstDigitValue << 4) + secondDigitValue));
146         }
147       } else {
148         unescaped.append(c);
149       }
150     }
151     return unescaped.toString();
152   }
153
154   private static int findFirstEscape(char[] escapedArray) {
155     int max = escapedArray.length;
156     for (int i = 0; i < max; i++) {
157       char c = escapedArray[i];
158       if (c == '+' || c == '%') {
159         return i;
160       }
161     }
162     return -1;
163   }
164
165   private static int parseHexDigit(char c) {
166     if (c >= 'a') {
167       if (c <= 'f') {
168         return 10 + (c - 'a');
169       }
170     } else if (c >= 'A') {
171       if (c <= 'F') {
172         return 10 + (c - 'A');
173       }
174     } else if (c >= '0') {
175       if (c <= '9') {
176         return c - '0';
177       }
178     }
179     return -1;
180   }
181
182   protected static Hashtable parseNameValuePairs(String uri) {
183     int paramStart = uri.indexOf('?');
184     if (paramStart < 0) {
185       return null;
186     }
187     Hashtable result = new Hashtable(3);
188     paramStart++;
189     int paramEnd;
190     while ((paramEnd = uri.indexOf('&', paramStart)) >= 0) {
191       appendKeyValue(uri, paramStart, paramEnd, result);
192       paramStart = paramEnd + 1;
193     }
194     appendKeyValue(uri, paramStart, uri.length(), result);
195     return result;
196   }
197
198   private static void appendKeyValue(String uri, int paramStart, int paramEnd, Hashtable result) {
199     int separator = uri.indexOf('=', paramStart);
200     if (separator >= 0) {
201       // key = value
202       String key = uri.substring(paramStart, separator);
203       String value = uri.substring(separator + 1, paramEnd);
204       value = urlDecode(value);
205       result.put(key, value);
206     } else {
207       // key, no value
208       String key = uri.substring(paramStart, paramEnd);
209       result.put(key, null);
210     }
211   }
212
213 }