/* * 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 = WifiResultParser.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; } }