mirror of
https://github.com/zxing/zxing.git
synced 2025-01-12 03:37:34 -08:00
More refactoring of parsed results / result parsers; added basic vCard support
git-svn-id: https://zxing.googlecode.com/svn/trunk@487 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
dd57042f9a
commit
aa65741586
|
@ -229,7 +229,7 @@ public final class BarcodeReaderCaptureActivity extends Activity {
|
|||
}
|
||||
|
||||
private static ParsedResult parseReaderResult(Result rawResult) {
|
||||
ParsedResult result = ResultParser.parseReaderResult(rawResult);
|
||||
ParsedResult result = ResultParser.parseResult(rawResult);
|
||||
if (result.getType().equals(ParsedResultType.TEXT)) {
|
||||
String rawText = rawResult.getText();
|
||||
AndroidIntentParsedResult androidResult = AndroidIntentParsedResult.parse(rawText);
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.client.result;
|
||||
|
||||
/**
|
||||
* <p>See
|
||||
* <a href="http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/about/s2.html">
|
||||
* DoCoMo's documentation</a> about the result types represented by subclasses of this class.</p>
|
||||
*
|
||||
* <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
|
||||
* on exception-based mechanisms during parsing.</p>
|
||||
*
|
||||
* @author srowen@google.com (Sean Owen)
|
||||
*/
|
||||
abstract class AbstractDoCoMoParsedResult extends ParsedResult {
|
||||
|
||||
AbstractDoCoMoParsedResult(ParsedResultType type) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
}
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package com.google.zxing.client.result;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* <p>See
|
||||
* <a href="http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/about/s2.html">
|
||||
|
@ -37,57 +35,8 @@ abstract class AbstractDoCoMoResultParser extends ResultParser {
|
|||
return matchPrefixedField(prefix, rawText, ';');
|
||||
}
|
||||
|
||||
static String[] matchPrefixedField(String prefix, String rawText, char endChar) {
|
||||
Vector matches = null;
|
||||
int i = 0;
|
||||
int max = rawText.length();
|
||||
while (i < max) {
|
||||
i = rawText.indexOf(prefix, i);
|
||||
if (i < 0) {
|
||||
break;
|
||||
}
|
||||
i += prefix.length(); // Skip past this prefix we found to start
|
||||
int start = i; // Found the start of a match here
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
i = rawText.indexOf((int) endChar, i);
|
||||
if (i < 0) {
|
||||
// No terminating end character? uh, done. Set i such that loop terminates and break
|
||||
i = rawText.length();
|
||||
done = true;
|
||||
} else if (rawText.charAt(i - 1) == '\\') {
|
||||
// semicolon was escaped so continue
|
||||
i++;
|
||||
} else {
|
||||
// found a match
|
||||
if (matches == null) {
|
||||
matches = new Vector(3); // lazy init
|
||||
}
|
||||
matches.addElement(unescapeBackslash(rawText.substring(start, i)));
|
||||
i++;
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matches == null || matches.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int size = matches.size();
|
||||
String[] result = new String[size];
|
||||
for (int j = 0; j < size; j++) {
|
||||
result[j] = (String) matches.elementAt(j);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static String matchSinglePrefixedField(String prefix, String rawText) {
|
||||
return matchSinglePrefixedField(prefix, rawText, ';');
|
||||
}
|
||||
|
||||
static String matchSinglePrefixedField(String prefix, String rawText, char endChar) {
|
||||
String[] matches = matchPrefixedField(prefix, rawText, endChar);
|
||||
return matches == null ? null : matches[0];
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -39,15 +39,15 @@ public final class AddressBookAUResultParser extends ResultParser {
|
|||
String[] names = matchMultipleValuePrefix("NAME", 2, rawText);
|
||||
String[] phoneNumbers = matchMultipleValuePrefix("TEL", 3, rawText);
|
||||
String[] emails = matchMultipleValuePrefix("MAIL", 3, rawText);
|
||||
String note = AbstractDoCoMoResultParser.matchSinglePrefixedField("MEMORY:", rawText, '\r');
|
||||
String address = AbstractDoCoMoResultParser.matchSinglePrefixedField("ADD:", rawText, '\r');
|
||||
String note = matchSinglePrefixedField("MEMORY:", rawText, '\r');
|
||||
String address = matchSinglePrefixedField("ADD:", rawText, '\r');
|
||||
return new AddressBookParsedResult(names, phoneNumbers, emails, note, address, null, null, null);
|
||||
}
|
||||
|
||||
private static String[] matchMultipleValuePrefix(String prefix, int max, String rawText) {
|
||||
Vector values = null;
|
||||
for (int i = 1; i <= max; i++) {
|
||||
String value = AbstractDoCoMoResultParser.matchSinglePrefixedField(prefix + i + ':', rawText, '\r');
|
||||
String value = matchSinglePrefixedField(prefix + i + ':', rawText, '\r');
|
||||
if (value == null) {
|
||||
break;
|
||||
}
|
||||
|
@ -59,11 +59,7 @@ public final class AddressBookAUResultParser extends ResultParser {
|
|||
if (values == null) {
|
||||
return null;
|
||||
}
|
||||
String[] result = new String[values.size()];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = (String) values.elementAt(i);
|
||||
}
|
||||
return result;
|
||||
return toStringArray(values);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ package com.google.zxing.client.result;
|
|||
/**
|
||||
* @author srowen@google.com (Sean Owen)
|
||||
*/
|
||||
public final class AddressBookParsedResult extends AbstractDoCoMoParsedResult {
|
||||
public final class AddressBookParsedResult extends ParsedResult {
|
||||
|
||||
private final String[] names;
|
||||
private final String[] phoneNumbers;
|
||||
|
@ -77,6 +77,9 @@ public final class AddressBookParsedResult extends AbstractDoCoMoParsedResult {
|
|||
return org;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return birthday formatted as yyyyMMdd (e.g. 19780917)
|
||||
*/
|
||||
public String getBirthday() {
|
||||
return birthday;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public final class CalendarParsedResult extends ParsedResult {
|
|||
* needs to work under JavaME / MIDP and there is no date parsing library available there, such
|
||||
* as <code>java.text.SimpleDateFormat</code>.</p>
|
||||
*
|
||||
* <p>Instead this is a String formatted as YYYYMMdd'T'HHmmss'Z'.</p>
|
||||
* @return start time formatted as yyyyMMdd'T'HHmmss'Z'.</p>
|
||||
*/
|
||||
public String getStart() {
|
||||
return start;
|
||||
|
|
|
@ -19,7 +19,7 @@ package com.google.zxing.client.result;
|
|||
/**
|
||||
* @author srowen@google.com (Sean Owen)
|
||||
*/
|
||||
public final class EmailAddressParsedResult extends AbstractDoCoMoParsedResult {
|
||||
public final class EmailAddressParsedResult extends ParsedResult {
|
||||
|
||||
private final String emailAddress;
|
||||
private final String subject;
|
||||
|
|
|
@ -19,11 +19,12 @@ package com.google.zxing.client.result;
|
|||
import com.google.zxing.Result;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* <p>Abstract class representing the result of decoding a barcode, as more than
|
||||
* a String -- as some type of structured data. This might be a subclass which represents
|
||||
* a URL, or an e-mail address. {@link #parseReaderResult(com.google.zxing.Result)} will turn a raw
|
||||
* a URL, or an e-mail address. {@link #parseResult(com.google.zxing.Result)} will turn a raw
|
||||
* decoded string into the most appropriate type of structured representation.</p>
|
||||
*
|
||||
* <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
|
||||
|
@ -33,7 +34,7 @@ import java.util.Hashtable;
|
|||
*/
|
||||
public abstract class ResultParser {
|
||||
|
||||
public static ParsedResult parseReaderResult(Result theResult) {
|
||||
public static ParsedResult parseResult(Result theResult) {
|
||||
// This is a bit messy, but given limited options in MIDP / CLDC, this may well be the simplest
|
||||
// way to go about this. For example, we have no reflection available, really.
|
||||
// Order is important here.
|
||||
|
@ -48,6 +49,8 @@ public abstract class ResultParser {
|
|||
return result;
|
||||
} else if ((result = AddressBookAUResultParser.parse(theResult)) != null) {
|
||||
return result;
|
||||
} else if ((result = VCardResultParser.parse(theResult)) != null) {
|
||||
return result;
|
||||
} else if ((result = TelResultParser.parse(theResult)) != null) {
|
||||
return result;
|
||||
} else if ((result = SMSResultParser.parse(theResult)) != null) {
|
||||
|
@ -179,6 +182,20 @@ public abstract class ResultParser {
|
|||
return -1;
|
||||
}
|
||||
|
||||
protected static boolean isStringOfDigits(String value, int length) {
|
||||
int stringLength = value.length();
|
||||
if (length != stringLength) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = value.charAt(i);
|
||||
if (c < '0' || c > '9') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static Hashtable parseNameValuePairs(String uri) {
|
||||
int paramStart = uri.indexOf('?');
|
||||
if (paramStart < 0) {
|
||||
|
@ -210,4 +227,56 @@ public abstract class ResultParser {
|
|||
}
|
||||
}
|
||||
|
||||
static String[] matchPrefixedField(String prefix, String rawText, char endChar) {
|
||||
Vector matches = null;
|
||||
int i = 0;
|
||||
int max = rawText.length();
|
||||
while (i < max) {
|
||||
i = rawText.indexOf(prefix, i);
|
||||
if (i < 0) {
|
||||
break;
|
||||
}
|
||||
i += prefix.length(); // Skip past this prefix we found to start
|
||||
int start = i; // Found the start of a match here
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
i = rawText.indexOf((int) endChar, i);
|
||||
if (i < 0) {
|
||||
// No terminating end character? uh, done. Set i such that loop terminates and break
|
||||
i = rawText.length();
|
||||
done = true;
|
||||
} else if (rawText.charAt(i - 1) == '\\') {
|
||||
// semicolon was escaped so continue
|
||||
i++;
|
||||
} else {
|
||||
// found a match
|
||||
if (matches == null) {
|
||||
matches = new Vector(3); // lazy init
|
||||
}
|
||||
matches.addElement(unescapeBackslash(rawText.substring(start, i)));
|
||||
i++;
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matches == null || matches.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return toStringArray(matches);
|
||||
}
|
||||
|
||||
static String matchSinglePrefixedField(String prefix, String rawText, char endChar) {
|
||||
String[] matches = matchPrefixedField(prefix, rawText, endChar);
|
||||
return matches == null ? null : matches[0];
|
||||
}
|
||||
|
||||
static String[] toStringArray(Vector strings) {
|
||||
int size = strings.size();
|
||||
String[] result = new String[size];
|
||||
for (int j = 0; j < size; j++) {
|
||||
result[j] = (String) strings.elementAt(j);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
161
core/src/com/google/zxing/client/result/VCardResultParser.java
Normal file
161
core/src/com/google/zxing/client/result/VCardResultParser.java
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.client.result;
|
||||
|
||||
import com.google.zxing.Result;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Parses contact information formatted according to the VCard (2.1) format. This is not a complete
|
||||
* implementation but should parse information as commonly encoded in 2D barcodes.
|
||||
*
|
||||
* @author srowen@google.com (Sean Owen)
|
||||
*/
|
||||
public final class VCardResultParser extends ResultParser {
|
||||
|
||||
private VCardResultParser() {
|
||||
}
|
||||
|
||||
public static AddressBookParsedResult parse(Result result) {
|
||||
String rawText = result.getText();
|
||||
if (rawText == null || !rawText.startsWith("BEGIN:VCARD") || !rawText.endsWith("END:VCARD")) {
|
||||
return null;
|
||||
}
|
||||
String[] names = matchVCardPrefixedField("FN", rawText);
|
||||
if (names == null) {
|
||||
// If no display names found, look for regular name fields and format them
|
||||
names = matchVCardPrefixedField("N", rawText);
|
||||
formatNames(names);
|
||||
}
|
||||
String[] phoneNumbers = matchVCardPrefixedField("TEL", rawText);
|
||||
String[] emails = matchVCardPrefixedField("EMAIL", rawText);
|
||||
String note = matchSingleVCardPrefixedField("NOTE", rawText);
|
||||
String address = matchSingleVCardPrefixedField("ADR", rawText);
|
||||
address = formatAddress(address);
|
||||
String org = matchSingleVCardPrefixedField("ORG", rawText);
|
||||
String birthday = matchSingleVCardPrefixedField("BDAY", rawText);
|
||||
if (!isStringOfDigits(birthday, 8)) {
|
||||
return null;
|
||||
}
|
||||
String title = matchSingleVCardPrefixedField("TITLE", rawText);
|
||||
return new AddressBookParsedResult(names, phoneNumbers, emails, note, address, org, birthday, title);
|
||||
}
|
||||
|
||||
private static String[] matchVCardPrefixedField(String prefix, String rawText) {
|
||||
Vector matches = null;
|
||||
int i = 0;
|
||||
int max = rawText.length();
|
||||
while (i < max) {
|
||||
i = rawText.indexOf(prefix, i);
|
||||
if (i < 0) {
|
||||
break;
|
||||
}
|
||||
if (rawText.charAt(i - 1) != '\n') {
|
||||
// then this didn't start a new token, we matched in the middle of something
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
i += prefix.length(); // Skip past this prefix we found to start
|
||||
if (rawText.charAt(i) != ':' && rawText.charAt(i) != ';') {
|
||||
continue;
|
||||
}
|
||||
while (rawText.charAt(i) != ':') { // Skip until a colon
|
||||
i++;
|
||||
}
|
||||
i++; // skip colon
|
||||
int start = i; // Found the start of a match here
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
i = rawText.indexOf((int) '\n', i); // Really, ends in \r\n
|
||||
if (i < 0) {
|
||||
// No terminating end character? uh, done. Set i such that loop terminates and break
|
||||
i = rawText.length();
|
||||
done = true;
|
||||
} else {
|
||||
// found a match
|
||||
if (matches == null) {
|
||||
matches = new Vector(3); // lazy init
|
||||
}
|
||||
matches.addElement(rawText.substring(start, i - 1)); // i - 1 to strip off the \r too
|
||||
i++;
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matches == null || matches.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return toStringArray(matches);
|
||||
}
|
||||
|
||||
private static String matchSingleVCardPrefixedField(String prefix, String rawText) {
|
||||
String[] values = matchVCardPrefixedField(prefix, rawText);
|
||||
return values == null ? null : values[0];
|
||||
}
|
||||
|
||||
private static String formatAddress(String address) {
|
||||
int length = address.length();
|
||||
StringBuffer newAddress = new StringBuffer(length);
|
||||
for (int j = 0; j < length; j++) {
|
||||
char c = address.charAt(j);
|
||||
if (c == ';') {
|
||||
newAddress.append(' ');
|
||||
} else {
|
||||
newAddress.append(c);
|
||||
}
|
||||
}
|
||||
return newAddress.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats name fields of the form "Public;John;Q.;Reverend;III" into a form like
|
||||
* "Reverend John Q. Public III".
|
||||
*
|
||||
* @param names name values to format, in place
|
||||
*/
|
||||
private static void formatNames(String[] names) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
String name = names[i];
|
||||
String[] components = new String[5];
|
||||
int start = 0;
|
||||
int end;
|
||||
int componentIndex = 0;
|
||||
while ((end = name.indexOf(';', start)) > 0) {
|
||||
components[componentIndex] = name.substring(start, end);
|
||||
componentIndex++;
|
||||
start = end + 1;
|
||||
}
|
||||
components[componentIndex] = name.substring(start);
|
||||
StringBuffer newName = new StringBuffer();
|
||||
maybeAppendComponent(components, 3, newName);
|
||||
maybeAppendComponent(components, 1, newName);
|
||||
maybeAppendComponent(components, 2, newName);
|
||||
maybeAppendComponent(components, 0, newName);
|
||||
maybeAppendComponent(components, 4, newName);
|
||||
names[i] = newName.toString().trim();
|
||||
}
|
||||
}
|
||||
|
||||
private static void maybeAppendComponent(String[] components, int i, StringBuffer newName) {
|
||||
if (components[i] != null) {
|
||||
newName.append(' ');
|
||||
newName.append(components[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -51,6 +51,9 @@ public final class MobileTagSimpleContactResultParser extends AbstractMobileTagR
|
|||
String address = matches[5];
|
||||
String org = matches[6];
|
||||
String birthday = matches[7];
|
||||
if (!isStringOfDigits(birthday, 8)) {
|
||||
return null;
|
||||
}
|
||||
String title = matches[8];
|
||||
|
||||
return new AddressBookParsedResult(new String[] {fullName},
|
||||
|
|
|
@ -145,14 +145,14 @@ public final class ParsedReaderResultTestCase extends TestCase {
|
|||
|
||||
private static void doTestResult(String text, ParsedResultType type, BarcodeFormat format) {
|
||||
Result fakeResult = new Result(text, null, null, format);
|
||||
ParsedResult result = ResultParser.parseReaderResult(fakeResult);
|
||||
ParsedResult result = ResultParser.parseResult(fakeResult);
|
||||
assertNotNull(result);
|
||||
assertEquals(type, result.getType());
|
||||
}
|
||||
|
||||
private static void doTestResult(byte[] rawBytes, ParsedResultType type) {
|
||||
Result fakeResult = new Result(null, rawBytes, null, null);
|
||||
ParsedResult result = ResultParser.parseReaderResult(fakeResult);
|
||||
ParsedResult result = ResultParser.parseResult(fakeResult);
|
||||
assertNotNull(result);
|
||||
assertEquals(type, result.getType());
|
||||
}
|
||||
|
|
|
@ -192,7 +192,7 @@ public final class ZXingMIDlet extends MIDlet {
|
|||
}
|
||||
|
||||
void handleDecodedText(Result theResult) {
|
||||
ParsedResult result = ResultParser.parseReaderResult(theResult);
|
||||
ParsedResult result = ResultParser.parseResult(theResult);
|
||||
ParsedResultType type = result.getType();
|
||||
if (type.equals(ParsedResultType.URI)) {
|
||||
String uri = ((URIParsedResult) result).getURI();
|
||||
|
|
|
@ -244,7 +244,7 @@ public final class DecodeServlet extends HttpServlet {
|
|||
} else {
|
||||
request.setAttribute("rawBytesString", "(Not applicable)");
|
||||
}
|
||||
ParsedResult parsedResult = ResultParser.parseReaderResult(result);
|
||||
ParsedResult parsedResult = ResultParser.parseResult(result);
|
||||
request.setAttribute("parsedResult", parsedResult);
|
||||
request.getRequestDispatcher("decoderesult.jspx").forward(request, response);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue