blob: f1b347611e5a3798877b9eceaa9841140f832c5d [file] [log] [blame]
/*
* 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;
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 #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
* on exception-based mechanisms during parsing.</p>
*
* @author Sean Owen
*/
public abstract class ResultParser {
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.
ParsedResult result;
if ((result = BookmarkDoCoMoResultParser.parse(theResult)) != null) {
return result;
} else if ((result = AddressBookDoCoMoResultParser.parse(theResult)) != null) {
return result;
} else if ((result = EmailDoCoMoResultParser.parse(theResult)) != null) {
return result;
} else if ((result = AddressBookAUResultParser.parse(theResult)) != null) {
return result;
} else if ((result = VCardResultParser.parse(theResult)) != null) {
return result;
} else if ((result = BizcardResultParser.parse(theResult)) != null) {
return result;
} else if ((result = VEventResultParser.parse(theResult)) != null) {
return result;
} else if ((result = EmailAddressResultParser.parse(theResult)) != null) {
return result;
} else if ((result = TelResultParser.parse(theResult)) != null) {
return result;
} else if ((result = SMSMMSResultParser.parse(theResult)) != null) {
return result;
} else if ((result = SMSTOMMSTOResultParser.parse(theResult)) != null) {
return result;
} else if ((result = GeoResultParser.parse(theResult)) != null) {
return result;
} else if ((result = URLTOResultParser.parse(theResult)) != null) {
return result;
} else if ((result = URIResultParser.parse(theResult)) != null) {
return result;
} else if ((result = ISBNResultParser.parse(theResult)) != null) {
// We depend on ISBN parsing coming before UPC, as it is a subset.
return result;
} else if ((result = ProductResultParser.parse(theResult)) != null) {
return result;
} else if ((result = ExpandedProductResultParser.parse(theResult)) != null) {
return result;
}
return new TextParsedResult(theResult.getText(), null);
}
protected static void maybeAppend(String value, StringBuffer result) {
if (value != null) {
result.append('\n');
result.append(value);
}
}
protected static void maybeAppend(String[] value, StringBuffer result) {
if (value != null) {
for (int i = 0; i < value.length; i++) {
result.append('\n');
result.append(value[i]);
}
}
}
protected static String[] maybeWrap(String value) {
return value == null ? null : new String[] { value };
}
protected static String unescapeBackslash(String escaped) {
if (escaped != null) {
int backslash = escaped.indexOf((int) '\\');
if (backslash >= 0) {
int max = escaped.length();
StringBuffer unescaped = new StringBuffer(max - 1);
unescaped.append(escaped.toCharArray(), 0, backslash);
boolean nextIsEscaped = false;
for (int i = backslash; i < max; i++) {
char c = escaped.charAt(i);
if (nextIsEscaped || c != '\\') {
unescaped.append(c);
nextIsEscaped = false;
} else {
nextIsEscaped = true;
}
}
return unescaped.toString();
}
}
return escaped;
}
private static String urlDecode(String escaped) {
// No we can't use java.net.URLDecoder here. JavaME doesn't have it.
if (escaped == null) {
return null;
}
char[] escapedArray = escaped.toCharArray();
int first = findFirstEscape(escapedArray);
if (first < 0) {
return escaped;
}
int max = escapedArray.length;
// final length is at most 2 less than original due to at least 1 unescaping
StringBuffer unescaped = new StringBuffer(max - 2);
// Can append everything up to first escape character
unescaped.append(escapedArray, 0, first);
for (int i = first; i < max; i++) {
char c = escapedArray[i];
if (c == '+') {
// + is translated directly into a space
unescaped.append(' ');
} else if (c == '%') {
// Are there even two more chars? if not we will just copy the escaped sequence and be done
if (i >= max - 2) {
unescaped.append('%'); // append that % and move on
} else {
int firstDigitValue = parseHexDigit(escapedArray[++i]);
int secondDigitValue = parseHexDigit(escapedArray[++i]);
if (firstDigitValue < 0 || secondDigitValue < 0) {
// bad digit, just move on
unescaped.append('%');
unescaped.append(escapedArray[i-1]);
unescaped.append(escapedArray[i]);
}
unescaped.append((char) ((firstDigitValue << 4) + secondDigitValue));
}
} else {
unescaped.append(c);
}
}
return unescaped.toString();
}
private static int findFirstEscape(char[] escapedArray) {
int max = escapedArray.length;
for (int i = 0; i < max; i++) {
char c = escapedArray[i];
if (c == '+' || c == '%') {
return i;
}
}
return -1;
}
private static int parseHexDigit(char c) {
if (c >= 'a') {
if (c <= 'f') {
return 10 + (c - 'a');
}
} else if (c >= 'A') {
if (c <= 'F') {
return 10 + (c - 'A');
}
} else if (c >= '0') {
if (c <= '9') {
return c - '0';
}
}
return -1;
}
protected static boolean isStringOfDigits(String value, int length) {
if (value == null) {
return false;
}
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 boolean isSubstringOfDigits(String value, int offset, int length) {
if (value == null) {
return false;
}
int stringLength = value.length();
int max = offset + length;
if (stringLength < max) {
return false;
}
for (int i = offset; i < max; i++) {
char c = value.charAt(i);
if (c < '0' || c > '9') {
return false;
}
}
return true;
}
static Hashtable parseNameValuePairs(String uri) {
int paramStart = uri.indexOf('?');
if (paramStart < 0) {
return null;
}
Hashtable result = new Hashtable(3);
paramStart++;
int paramEnd;
while ((paramEnd = uri.indexOf('&', paramStart)) >= 0) {
appendKeyValue(uri, paramStart, paramEnd, result);
paramStart = paramEnd + 1;
}
appendKeyValue(uri, paramStart, uri.length(), result);
return result;
}
private static void appendKeyValue(String uri, int paramStart, int paramEnd, Hashtable result) {
int separator = uri.indexOf('=', paramStart);
if (separator >= 0) {
// key = value
String key = uri.substring(paramStart, separator);
String value = uri.substring(separator + 1, paramEnd);
value = urlDecode(value);
result.put(key, value);
}
// Can't put key, null into a hashtable
}
static String[] matchPrefixedField(String prefix, String rawText, char endChar, boolean trim) {
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
}
String element = unescapeBackslash(rawText.substring(start, i));
if (trim) {
element = element.trim();
}
matches.addElement(element);
i++;
done = true;
}
}
}
if (matches == null || matches.isEmpty()) {
return null;
}
return toStringArray(matches);
}
static String matchSinglePrefixedField(String prefix, String rawText, char endChar, boolean trim) {
String[] matches = matchPrefixedField(prefix, rawText, endChar, trim);
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;
}
}