mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
262 lines
8.1 KiB
Java
262 lines
8.1 KiB
Java
/*
|
|
* 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.RXingResult;
|
|
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.net.URLDecoder;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.regex.Pattern;
|
|
|
|
/**
|
|
* <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 #parseRXingResult(RXingResult)} 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 RXingResultParser {
|
|
|
|
private static final RXingResultParser[] PARSERS = {
|
|
new BookmarkDoCoMoRXingResultParser(),
|
|
new AddressBookDoCoMoRXingResultParser(),
|
|
new EmailDoCoMoRXingResultParser(),
|
|
new AddressBookAURXingResultParser(),
|
|
new VCardRXingResultParser(),
|
|
new BizcardRXingResultParser(),
|
|
new VEventRXingResultParser(),
|
|
new EmailAddressRXingResultParser(),
|
|
new SMTPRXingResultParser(),
|
|
new TelRXingResultParser(),
|
|
new SMSMMSRXingResultParser(),
|
|
new SMSTOMMSTORXingResultParser(),
|
|
new GeoRXingResultParser(),
|
|
new WifiRXingResultParser(),
|
|
new URLTORXingResultParser(),
|
|
new URIRXingResultParser(),
|
|
new ISBNRXingResultParser(),
|
|
new ProductRXingResultParser(),
|
|
new ExpandedProductRXingResultParser(),
|
|
new VINRXingResultParser(),
|
|
};
|
|
|
|
private static final Pattern DIGITS = Pattern.compile("\\d+");
|
|
private static final Pattern AMPERSAND = Pattern.compile("&");
|
|
private static final Pattern EQUALS = Pattern.compile("=");
|
|
private static final String BYTE_ORDER_MARK = "\ufeff";
|
|
|
|
static final String[] EMPTY_STR_ARRAY = new String[0];
|
|
|
|
/**
|
|
* Attempts to parse the raw {@link RXingResult}'s contents as a particular type
|
|
* of information (email, URL, etc.) and return a {@link ParsedRXingResult} encapsulating
|
|
* the result of parsing.
|
|
*
|
|
* @param theRXingResult the raw {@link RXingResult} to parse
|
|
* @return {@link ParsedRXingResult} encapsulating the parsing result
|
|
*/
|
|
public abstract ParsedRXingResult parse(RXingResult theRXingResult);
|
|
|
|
protected static String getMassagedText(RXingResult result) {
|
|
String text = result.getText();
|
|
if (text.startsWith(BYTE_ORDER_MARK)) {
|
|
text = text.substring(1);
|
|
}
|
|
return text;
|
|
}
|
|
|
|
public static ParsedRXingResult parseRXingResult(RXingResult theRXingResult) {
|
|
for (RXingResultParser parser : PARSERS) {
|
|
ParsedRXingResult result = parser.parse(theRXingResult);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
}
|
|
return new TextParsedRXingResult(theRXingResult.getText(), null);
|
|
}
|
|
|
|
protected static void maybeAppend(String value, StringBuilder result) {
|
|
if (value != null) {
|
|
result.append('\n');
|
|
result.append(value);
|
|
}
|
|
}
|
|
|
|
protected static void maybeAppend(String[] value, StringBuilder result) {
|
|
if (value != null) {
|
|
for (String s : value) {
|
|
result.append('\n');
|
|
result.append(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected static String[] maybeWrap(String value) {
|
|
return value == null ? null : new String[] { value };
|
|
}
|
|
|
|
protected static String unescapeBackslash(String escaped) {
|
|
int backslash = escaped.indexOf('\\');
|
|
if (backslash < 0) {
|
|
return escaped;
|
|
}
|
|
int max = escaped.length();
|
|
StringBuilder unescaped = new StringBuilder(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();
|
|
}
|
|
|
|
protected static int parseHexDigit(char c) {
|
|
if (c >= '0' && c <= '9') {
|
|
return c - '0';
|
|
}
|
|
if (c >= 'a' && c <= 'f') {
|
|
return 10 + (c - 'a');
|
|
}
|
|
if (c >= 'A' && c <= 'F') {
|
|
return 10 + (c - 'A');
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
protected static boolean isStringOfDigits(CharSequence value, int length) {
|
|
return value != null && length > 0 && length == value.length() && DIGITS.matcher(value).matches();
|
|
}
|
|
|
|
protected static boolean isSubstringOfDigits(CharSequence value, int offset, int length) {
|
|
if (value == null || length <= 0) {
|
|
return false;
|
|
}
|
|
int max = offset + length;
|
|
return value.length() >= max && DIGITS.matcher(value.subSequence(offset, max)).matches();
|
|
}
|
|
|
|
static Map<String,String> parseNameValuePairs(String uri) {
|
|
int paramStart = uri.indexOf('?');
|
|
if (paramStart < 0) {
|
|
return null;
|
|
}
|
|
Map<String,String> result = new HashMap<>(3);
|
|
for (String keyValue : AMPERSAND.split(uri.substring(paramStart + 1))) {
|
|
appendKeyValue(keyValue, result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static void appendKeyValue(CharSequence keyValue, Map<String,String> result) {
|
|
String[] keyValueTokens = EQUALS.split(keyValue, 2);
|
|
if (keyValueTokens.length == 2) {
|
|
String key = keyValueTokens[0];
|
|
String value = keyValueTokens[1];
|
|
try {
|
|
value = urlDecode(value);
|
|
result.put(key, value);
|
|
} catch (IllegalArgumentException iae) {
|
|
// continue; invalid data such as an escape like %0t
|
|
}
|
|
}
|
|
}
|
|
|
|
static String urlDecode(String encoded) {
|
|
try {
|
|
return URLDecoder.decode(encoded, "UTF-8");
|
|
} catch (UnsupportedEncodingException uee) {
|
|
throw new IllegalStateException(uee); // can't happen
|
|
}
|
|
}
|
|
|
|
static String[] matchPrefixedField(String prefix, String rawText, char endChar, boolean trim) {
|
|
List<String> 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 more = true;
|
|
while (more) {
|
|
i = rawText.indexOf(endChar, i);
|
|
if (i < 0) {
|
|
// No terminating end character? uh, done. Set i such that loop terminates and break
|
|
i = rawText.length();
|
|
more = false;
|
|
} else if (countPrecedingBackslashes(rawText, i) % 2 != 0) {
|
|
// semicolon was escaped (odd count of preceding backslashes) so continue
|
|
i++;
|
|
} else {
|
|
// found a match
|
|
if (matches == null) {
|
|
matches = new ArrayList<>(3); // lazy init
|
|
}
|
|
String element = unescapeBackslash(rawText.substring(start, i));
|
|
if (trim) {
|
|
element = element.trim();
|
|
}
|
|
if (!element.isEmpty()) {
|
|
matches.add(element);
|
|
}
|
|
i++;
|
|
more = false;
|
|
}
|
|
}
|
|
}
|
|
if (matches == null || matches.isEmpty()) {
|
|
return null;
|
|
}
|
|
return matches.toArray(EMPTY_STR_ARRAY);
|
|
}
|
|
|
|
private static int countPrecedingBackslashes(CharSequence s, int pos) {
|
|
int count = 0;
|
|
for (int i = pos - 1; i >= 0; i--) {
|
|
if (s.charAt(i) == '\\') {
|
|
count++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static String matchSinglePrefixedField(String prefix, String rawText, char endChar, boolean trim) {
|
|
String[] matches = matchPrefixedField(prefix, rawText, endChar, trim);
|
|
return matches == null ? null : matches[0];
|
|
}
|
|
|
|
}
|