2 * Copyright 2008 ZXing authors
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.google.zxing.client.result;
19 import com.google.zxing.Result;
21 import java.util.Vector;
24 * Parses contact information formatted according to the VCard (2.1) format. This is not a complete
25 * implementation but should parse information as commonly encoded in 2D barcodes.
29 final class VCardResultParser extends ResultParser {
31 private VCardResultParser() {
34 public static AddressBookParsedResult parse(Result result) {
35 // Although we should insist on the raw text ending with "END:VCARD", there's no reason
36 // to throw out everything else we parsed just because this was omitted. In fact, Eclair
37 // is doing just that, and we can't parse its contacts without this leniency.
38 String rawText = result.getText();
39 if (rawText == null || !rawText.startsWith("BEGIN:VCARD")) {
42 String[] names = matchVCardPrefixedField("FN", rawText, true);
44 // If no display names found, look for regular name fields and format them
45 names = matchVCardPrefixedField("N", rawText, true);
48 String[] phoneNumbers = matchVCardPrefixedField("TEL", rawText, true);
49 String[] emails = matchVCardPrefixedField("EMAIL", rawText, true);
50 String note = matchSingleVCardPrefixedField("NOTE", rawText, false);
51 String address = matchSingleVCardPrefixedField("ADR", rawText, true);
52 address = formatAddress(address);
53 String org = matchSingleVCardPrefixedField("ORG", rawText, true);
54 String birthday = matchSingleVCardPrefixedField("BDAY", rawText, true);
55 if (birthday != null && !isStringOfDigits(birthday, 8)) {
58 String title = matchSingleVCardPrefixedField("TITLE", rawText, true);
59 String url = matchSingleVCardPrefixedField("URL", rawText, true);
60 return new AddressBookParsedResult(names, null, phoneNumbers, emails, note, address, org,
61 birthday, title, url);
64 private static String[] matchVCardPrefixedField(String prefix, String rawText, boolean trim) {
65 Vector matches = null;
67 int max = rawText.length();
69 i = rawText.indexOf(prefix, i);
73 if (i > 0 && rawText.charAt(i - 1) != '\n') {
74 // then this didn't start a new token, we matched in the middle of something
78 i += prefix.length(); // Skip past this prefix we found to start
79 if (rawText.charAt(i) != ':' && rawText.charAt(i) != ';') {
82 while (rawText.charAt(i) != ':') { // Skip until a colon
86 int start = i; // Found the start of a match here
87 i = rawText.indexOf((int) '\n', i); // Really, ends in \r\n
89 // No terminating end character? uh, done. Set i such that loop terminates and break
91 } else if (i > start) {
93 if (matches == null) {
94 matches = new Vector(3); // lazy init
96 String element = rawText.substring(start, i);
98 element = element.trim();
100 matches.addElement(element);
106 if (matches == null || matches.isEmpty()) {
109 return toStringArray(matches);
112 static String matchSingleVCardPrefixedField(String prefix, String rawText, boolean trim) {
113 String[] values = matchVCardPrefixedField(prefix, rawText, trim);
114 return values == null ? null : values[0];
117 private static String formatAddress(String address) {
118 if (address == null) {
121 int length = address.length();
122 StringBuffer newAddress = new StringBuffer(length);
123 for (int j = 0; j < length; j++) {
124 char c = address.charAt(j);
126 newAddress.append(' ');
128 newAddress.append(c);
131 return newAddress.toString().trim();
135 * Formats name fields of the form "Public;John;Q.;Reverend;III" into a form like
136 * "Reverend John Q. Public III".
138 * @param names name values to format, in place
140 private static void formatNames(String[] names) {
142 for (int i = 0; i < names.length; i++) {
143 String name = names[i];
144 String[] components = new String[5];
147 int componentIndex = 0;
148 while ((end = name.indexOf(';', start)) > 0) {
149 components[componentIndex] = name.substring(start, end);
153 components[componentIndex] = name.substring(start);
154 StringBuffer newName = new StringBuffer(100);
155 maybeAppendComponent(components, 3, newName);
156 maybeAppendComponent(components, 1, newName);
157 maybeAppendComponent(components, 2, newName);
158 maybeAppendComponent(components, 0, newName);
159 maybeAppendComponent(components, 4, newName);
160 names[i] = newName.toString().trim();
165 private static void maybeAppendComponent(String[] components, int i, StringBuffer newName) {
166 if (components[i] != null) {
168 newName.append(components[i]);