One more change to accommodate differences in jpeg libraries. Some machines will...
[zxing.git] / csharp / client / result / VCardResultParser.cs
1 /*\r
2 * Copyright 2008 ZXing authors\r
3 *\r
4 * Licensed under the Apache License, Version 2.0 (the "License");\r
5 * you may not use this file except in compliance with the License.\r
6 * You may obtain a copy of the License at\r
7 *\r
8 *      http://www.apache.org/licenses/LICENSE-2.0\r
9 *\r
10 * Unless required by applicable law or agreed to in writing, software\r
11 * distributed under the License is distributed on an "AS IS" BASIS,\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13 * See the License for the specific language governing permissions and\r
14 * limitations under the License.\r
15 */\r
16 using System;\r
17 using Result = com.google.zxing.Result;\r
18 namespace com.google.zxing.client.result\r
19 {\r
20         \r
21         /// <summary> Parses contact information formatted according to the VCard (2.1) format. This is not a complete\r
22         /// implementation but should parse information as commonly encoded in 2D barcodes.\r
23         /// \r
24         /// </summary>\r
25         /// <author>  Sean Owen\r
26         /// </author>\r
27         /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
28         /// </author>\r
29         sealed class VCardResultParser:ResultParser\r
30         {\r
31                 \r
32                 private VCardResultParser()\r
33                 {\r
34                 }\r
35                 \r
36                 public static AddressBookParsedResult parse(Result result)\r
37                 {\r
38                         // Although we should insist on the raw text ending with "END:VCARD", there's no reason\r
39                         // to throw out everything else we parsed just because this was omitted. In fact, Eclair\r
40                         // is doing just that, and we can't parse its contacts without this leniency.\r
41                         System.String rawText = result.Text;\r
42                         if (rawText == null || !rawText.StartsWith("BEGIN:VCARD"))\r
43                         {\r
44                                 return null;\r
45                         }\r
46                         System.String[] names = matchVCardPrefixedField("FN", rawText, true);\r
47                         if (names == null)\r
48                         {\r
49                                 // If no display names found, look for regular name fields and format them\r
50                                 names = matchVCardPrefixedField("N", rawText, true);\r
51                                 formatNames(names);\r
52                         }\r
53                         System.String[] phoneNumbers = matchVCardPrefixedField("TEL", rawText, true);\r
54                         System.String[] emails = matchVCardPrefixedField("EMAIL", rawText, true);\r
55                         System.String note = matchSingleVCardPrefixedField("NOTE", rawText, false);\r
56                         System.String[] addresses = matchVCardPrefixedField("ADR", rawText, true);\r
57                         if (addresses != null)\r
58                         {\r
59                                 for (int i = 0; i < addresses.Length; i++)\r
60                                 {\r
61                                         addresses[i] = formatAddress(addresses[i]);\r
62                                 }\r
63                         }\r
64                         System.String org = matchSingleVCardPrefixedField("ORG", rawText, true);\r
65                         System.String birthday = matchSingleVCardPrefixedField("BDAY", rawText, true);\r
66                         if (!isLikeVCardDate(birthday))\r
67                         {\r
68                                 birthday = null;\r
69                         }\r
70                         System.String title = matchSingleVCardPrefixedField("TITLE", rawText, true);\r
71                         System.String url = matchSingleVCardPrefixedField("URL", rawText, true);\r
72                         return new AddressBookParsedResult(names, null, phoneNumbers, emails, note, addresses, org, birthday, title, url);\r
73                 }\r
74                 \r
75                 private static System.String[] matchVCardPrefixedField(System.String prefix, System.String rawText, bool trim)\r
76                 {\r
77                         System.Collections.ArrayList matches = null;\r
78                         int i = 0;\r
79                         int max = rawText.Length;\r
80                         while (i < max)\r
81                         {\r
82                                 //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'"\r
83                                 i = rawText.IndexOf(prefix, i);\r
84                                 if (i < 0)\r
85                                 {\r
86                                         break;\r
87                                 }\r
88                                 if (i > 0 && rawText[i - 1] != '\n')\r
89                                 {\r
90                                         // then this didn't start a new token, we matched in the middle of something\r
91                                         i++;\r
92                                         continue;\r
93                                 }\r
94                                 i += prefix.Length; // Skip past this prefix we found to start\r
95                                 if (rawText[i] != ':' && rawText[i] != ';')\r
96                                 {\r
97                                         continue;\r
98                                 }\r
99                                 while (rawText[i] != ':')\r
100                                 {\r
101                                         // Skip until a colon\r
102                                         i++;\r
103                                 }\r
104                                 i++; // skip colon\r
105                                 int start = i; // Found the start of a match here\r
106                                 //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'"\r
107                                 i = rawText.IndexOf('\n', i); // Really, ends in \r\n\r
108                                 if (i < 0)\r
109                                 {\r
110                                         // No terminating end character? uh, done. Set i such that loop terminates and break\r
111                                         i = max;\r
112                                 }\r
113                                 else if (i > start)\r
114                                 {\r
115                                         // found a match\r
116                                         if (matches == null)\r
117                                         {\r
118                                                 matches = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(3)); // lazy init\r
119                                         }\r
120                                         System.String element = rawText.Substring(start, (i) - (start));\r
121                                         if (trim)\r
122                                         {\r
123                                                 element = element.Trim();\r
124                                         }\r
125                                         matches.Add(element);\r
126                                         i++;\r
127                                 }\r
128                                 else\r
129                                 {\r
130                                         i++;\r
131                                 }\r
132                         }\r
133                         if (matches == null || (matches.Count == 0))\r
134                         {\r
135                                 return null;\r
136                         }\r
137                         return toStringArray(matches);\r
138                 }\r
139                 \r
140                 internal static System.String matchSingleVCardPrefixedField(System.String prefix, System.String rawText, bool trim)\r
141                 {\r
142                         System.String[] values = matchVCardPrefixedField(prefix, rawText, trim);\r
143                         return values == null?null:values[0];\r
144                 }\r
145                 \r
146                 private static bool isLikeVCardDate(System.String value_Renamed)\r
147                 {\r
148                         if (value_Renamed == null)\r
149                         {\r
150                                 return true;\r
151                         }\r
152                         // Not really sure this is true but matches practice\r
153                         // Mach YYYYMMDD\r
154                         if (isStringOfDigits(value_Renamed, 8))\r
155                         {\r
156                                 return true;\r
157                         }\r
158                         // or YYYY-MM-DD\r
159                         return value_Renamed.Length == 10 && value_Renamed[4] == '-' && value_Renamed[7] == '-' && isSubstringOfDigits(value_Renamed, 0, 4) && isSubstringOfDigits(value_Renamed, 5, 2) && isSubstringOfDigits(value_Renamed, 8, 2);\r
160                 }\r
161                 \r
162                 private static System.String formatAddress(System.String address)\r
163                 {\r
164                         if (address == null)\r
165                         {\r
166                                 return null;\r
167                         }\r
168                         int length = address.Length;\r
169                         System.Text.StringBuilder newAddress = new System.Text.StringBuilder(length);\r
170                         for (int j = 0; j < length; j++)\r
171                         {\r
172                                 char c = address[j];\r
173                                 if (c == ';')\r
174                                 {\r
175                                         newAddress.Append(' ');\r
176                                 }\r
177                                 else\r
178                                 {\r
179                                         newAddress.Append(c);\r
180                                 }\r
181                         }\r
182                         return newAddress.ToString().Trim();\r
183                 }\r
184                 \r
185                 /// <summary> Formats name fields of the form "Public;John;Q.;Reverend;III" into a form like\r
186                 /// "Reverend John Q. Public III".\r
187                 /// \r
188                 /// </summary>\r
189                 /// <param name="names">name values to format, in place\r
190                 /// </param>\r
191                 private static void  formatNames(System.String[] names)\r
192                 {\r
193                         if (names != null)\r
194                         {\r
195                                 for (int i = 0; i < names.Length; i++)\r
196                                 {\r
197                                         System.String name = names[i];\r
198                                         System.String[] components = new System.String[5];\r
199                                         int start = 0;\r
200                                         int end;\r
201                                         int componentIndex = 0;\r
202                                         //UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'"\r
203                                         while ((end = name.IndexOf(';', start)) > 0)\r
204                                         {\r
205                                                 components[componentIndex] = name.Substring(start, (end) - (start));\r
206                                                 componentIndex++;\r
207                                                 start = end + 1;\r
208                                         }\r
209                                         components[componentIndex] = name.Substring(start);\r
210                                         System.Text.StringBuilder newName = new System.Text.StringBuilder(100);\r
211                                         maybeAppendComponent(components, 3, newName);\r
212                                         maybeAppendComponent(components, 1, newName);\r
213                                         maybeAppendComponent(components, 2, newName);\r
214                                         maybeAppendComponent(components, 0, newName);\r
215                                         maybeAppendComponent(components, 4, newName);\r
216                                         names[i] = newName.ToString().Trim();\r
217                                 }\r
218                         }\r
219                 }\r
220                 \r
221                 private static void  maybeAppendComponent(System.String[] components, int i, System.Text.StringBuilder newName)\r
222                 {\r
223                         if (components[i] != null)\r
224                         {\r
225                                 newName.Append(' ');\r
226                                 newName.Append(components[i]);\r
227                         }\r
228                 }\r
229         }\r
230 }