Start port of client result types

This commit is contained in:
Henry Schimke
2022-09-02 19:00:40 -05:00
parent 833e7a5347
commit c66da802b5
8 changed files with 378 additions and 288 deletions

View File

@@ -9,9 +9,10 @@ exclude = [
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
regex = "1"
regex = "1.6"
encoding = "0.2"
rand = "0.8.5"
urlencoding = "2.1.2"
image = {version = "0.24.3", optional = true}
imageproc = {version = "0.23.0", optional = true}

1
src/client/mod.rs Normal file
View File

@@ -0,0 +1 @@
mod result;

View File

@@ -14,7 +14,9 @@
* limitations under the License.
*/
package com.google.zxing.client.result;
// package com.google.zxing.client.result;
use super::ParsedRXingResultType;
/**
* <p>Abstract class representing the result of decoding a barcode, as more than
@@ -27,39 +29,33 @@ package com.google.zxing.client.result;
*
* @author Sean Owen
*/
public abstract class ParsedRXingResult {
pub trait ParsedRXingResult {
private final ParsedRXingResultType type;
// private final ParsedRXingResultType type;
protected ParsedRXingResult(ParsedRXingResultType type) {
this.type = type;
}
// protected ParsedRXingResult(ParsedRXingResultType type) {
// this.type = type;
// }
public final ParsedRXingResultType getType() {
return type;
}
fn getType(&self) -> ParsedRXingResultType;
public abstract String getDisplayRXingResult();
fn getDisplayRXingResult(&self) -> String;
@Override
public final String toString() {
return getDisplayRXingResult();
}
public static void maybeAppend(String value, StringBuilder result) {
if (value != null && !value.isEmpty()) {
fn maybe_append(&self, value :&str, result:&mut String) {
if !value.is_empty() {
// Don't add a newline before the first value
if (result.length() > 0) {
result.append('\n');
if result.len() > 0 {
result.push('\n');
}
result.append(value);
result.push_str(value);
}
}
public static void maybeAppend(String[] values, StringBuilder result) {
if (values != null) {
for (String value : values) {
maybeAppend(value, result);
fn maybe_append_multiple(&self, values:&[&str], result:&mut String) {
if !values.is_empty() {
for value in values {
// for (String value : values) {
self.maybe_append(value, result);
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.google.zxing.client.result;
// package com.google.zxing.client.result;
/**
* Represents the type of data encoded by a barcode -- from plain text, to a
@@ -22,7 +22,7 @@ package com.google.zxing.client.result;
*
* @author Sean Owen
*/
public enum ParsedRXingResultType {
pub enum ParsedRXingResultType {
ADDRESSBOOK,
EMAIL_ADDRESS,

View File

@@ -1,261 +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;
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];
}
}

View File

@@ -0,0 +1,345 @@
/*
* 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;
use std::collections::HashMap;
use regex::Regex;
use urlencoding::decode;
use crate::{exceptions::Exceptions, RXingResult};
use super::ParsedRXingResult;
/**
* <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
*/
pub trait 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(),
// };
const DIGITS: &'static str = "\\d+"; //= Pattern.compile("\\d+");
const AMPERSAND: &'static str = "&"; // private static final Pattern AMPERSAND = Pattern.compile("&");
const EQUALS: &'static str = "="; //private static final Pattern EQUALS = Pattern.compile("=");
const BYTE_ORDER_MARK: &'static str = "\u{feff}"; //private static final String BYTE_ORDER_MARK = "\ufeff";
const EMPTY_STR_ARRAY: &'static str = "";
const PARSERS: [&'static str; 20] = [
"BookmarkDoCoMoRXingResultParser",
"AddressBookDoCoMoRXingResultParser",
"EmailDoCoMoRXingResultParser",
"AddressBookAURXingResultParser",
"VCardRXingResultParser",
"BizcardRXingResultParser",
"VEventRXingResultParser",
"EmailAddressRXingResultParser",
"SMTPRXingResultParser",
"TelRXingResultParser",
"SMSMMSRXingResultParser",
"SMSTOMMSTORXingResultParser",
"GeoRXingResultParser",
"WifiRXingResultParser",
"URLTORXingResultParser",
"URIRXingResultParser",
"ISBNRXingResultParser",
"ProductRXingResultParser",
"ExpandedProductRXingResultParser",
"VINRXingResultParser",
];
/**
* 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
*/
fn parse(&self, theRXingResult: &RXingResult) -> Self;
fn getMassagedText(result: &RXingResult) -> String {
let mut text = result.getText().clone();
if text.starts_with(Self::BYTE_ORDER_MARK) {
text = text[1..].to_owned();
}
return text;
}
fn parseRXingResult(theRXingResult: &RXingResult) -> Box<dyn ParsedRXingResult> {
for parser in Self::PARSERS {
// for (RXingResultParser parser : PARSERS) {
todo!("implement multiple parsers");
}
// ParsedRXingResult result = parser.parse(theRXingResult);
// if (result != null) {
// return result;
// }
// }
// return new TextParsedRXingResult(theRXingResult.getText(), null);
unimplemented!();
}
fn maybe_ppend_string(value: &str, result: &mut String) {
if !value.is_empty() {
result.push('\n');
result.push_str(value);
}
}
fn maybe_append_multiple(value: &[&str], result: &mut String) {
if !value.is_empty() {
for s in value {
// for (String s : value) {
result.push('\n');
result.push_str(s);
}
}
}
fn maybeWrap(value: Option<&str>) -> Option<Vec<String>> {
if value.is_none() {
None
} else {
Some(vec![value.unwrap().to_owned()])
}
}
fn unescapeBackslash(escaped: &str) -> String {
let backslash = escaped.find('\\');
if backslash.is_none() {
return escaped.to_owned();
}
let max = escaped.len();
let mut unescaped = String::with_capacity(max - 1);
let backslash = backslash.unwrap_or(0);
unescaped.push_str(&escaped[0..backslash]);
let mut nextIsEscaped = false;
for i in backslash..max {
// for (int i = backslash; i < max; i++) {
let c = escaped.chars().nth(i).unwrap();
if nextIsEscaped || c != '\\' {
unescaped.push(c);
nextIsEscaped = false;
} else {
nextIsEscaped = true;
}
}
unescaped
}
fn parseHexDigit(c: char) -> i32 {
if c >= '0' && c <= '9' {
return (c as u8 - '0' as u8) as i32;
}
if c >= 'a' && c <= 'f' {
return 10 + (c as u8 - 'a' as u8) as i32;
}
if c >= 'A' && c <= 'F' {
return 10 + (c as u8 - 'A' as u8) as i32;
}
return -1;
}
fn isStringOfDigits(value: &str, length: usize) -> bool {
let matcher = Regex::new(Self::DIGITS).unwrap();
!value.is_empty() && length > 0 && length == value.len() && matcher.is_match(value)
}
fn isSubstringOfDigits(value: &str, offset: i32, length: usize) -> bool {
if value.is_empty() || length <= 0 {
return false;
}
let max = offset as usize + length;
let matcher = Regex::new(Self::DIGITS).unwrap();
let sub_seq = &value[offset as usize..offset as usize + max];
value.len() >= max && matcher.is_match(sub_seq)
}
fn parseNameValuePairs(uri: &str) -> Option<HashMap<String, String>> {
let paramStart = uri.find('?');
if paramStart.is_none() {
return None;
}
let mut result = HashMap::with_capacity(3);
let paramStart = paramStart.unwrap_or(0);
let sub_str = &uri[paramStart + 1..];
let list = sub_str.split(Self::AMPERSAND);
for keyValue in list {
Self::appendKeyValue(keyValue, &mut result);
}
// for keyValue in Self::AMPERSAND.split(uri[paramStart + 1..]) {
// // for (String keyValue : AMPERSAND.split(uri.substring(paramStart + 1))) {
// Self::appendKeyValue(keyValue, &mut result);
// }
Some(result)
}
fn appendKeyValue(keyValue: &str, result: &mut HashMap<String, String>) {
let keyValueTokens = keyValue.split(Self::EQUALS); //Self::EQUALS.split(keyValue, 2);
let kvp: Vec<&str> = keyValueTokens.take(2).collect();
if let [key, value] = kvp[..] {
let p_value = Self::urlDecode(value).unwrap_or("".to_owned());
result.insert(key.to_owned(), p_value);
}
// if keyValueTokens.len() == 2 {
// let key = keyValueTokens[0];
// let value = keyValueTokens[1];
// try {
// value = Self::urlDecode(value);
// result.put(key, value);
// } catch (IllegalArgumentException iae) {
// // continue; invalid data such as an escape like %0t
// }
// }
}
fn urlDecode(encoded: &str) -> Result<String, Exceptions> {
if let Ok(decoded) = decode(encoded) {
Ok(decoded.to_string())
} else {
Err(Exceptions::IllegalStateException(
"UnsupportedEncodingException".to_owned(),
))
}
}
fn matchPrefixedField(
prefix: &str,
rawText: &str,
endChar: char,
trim: bool,
) -> Option<Vec<String>> {
let mut matches: Vec<String> = Vec::new();
let mut i = 0;
let max = rawText.len();
while i < max {
i = if let Some(loc) = rawText[i..].find(prefix) {
loc //rawText.indexOf(prefix, i);
} else {
break;
};
// if i < 0 {
// break;
// }
i += prefix.len(); // Skip past this prefix we found to start
let start = i; // Found the start of a match here
let mut more = true;
while more {
let i = if let Some(loc) = rawText[i..].find(endChar) {
if Self::countPrecedingBackslashes(rawText, i) % 2 != 0 {
// semicolon was escaped (odd count of preceding backslashes) so continue
i + 1
} else {
// found a match
let mut element = Self::unescapeBackslash(&rawText[start..i + start]);
if trim {
element = element.to_string().trim().to_owned();
}
if !element.is_empty() {
matches.push(element);
}
more = false;
i + 1
}
} else {
// No terminating end character? uh, done. Set i such that loop terminates and break
//i = rawText.len();
more = false;
rawText.len()
};
}
}
if matches.is_empty() {
return None;
}
Some(matches)
}
fn countPrecedingBackslashes(s: &str, pos: usize) -> u32 {
let mut count = 0;
for i in (0..pos - 1).rev() {
// for (int i = pos - 1; i >= 0; i--) {
if s.chars().nth(i).unwrap() == '\\' {
count += 1;
} else {
break;
}
}
return count;
}
fn matchSinglePrefixedField(
prefix: &str,
rawText: &str,
endChar: char,
trim: bool,
) -> Option<String> {
let matches = Self::matchPrefixedField(prefix, rawText, endChar, trim);
if let Some(m) = matches {
Some(m[0].clone())
} else {
None
}
// return matches == null ? null : matches[0];
}
}

7
src/client/result/mod.rs Normal file
View File

@@ -0,0 +1,7 @@
mod ParsedResult;
pub use ParsedResult::*;
mod ResultParser;
mod ParsedResultType;
pub use ParsedResultType::*;
pub use ResultParser::*;

View File

@@ -1,5 +1,6 @@
mod common;
mod exceptions;
mod client;
#[cfg(feature="image")]
mod BufferedImageLuminanceSource;