mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Add VIN result type
This commit is contained in:
parent
3f6ee7f789
commit
9983ccc428
|
@ -35,5 +35,6 @@ public enum ParsedResultType {
|
||||||
CALENDAR,
|
CALENDAR,
|
||||||
WIFI,
|
WIFI,
|
||||||
ISBN,
|
ISBN,
|
||||||
|
VIN,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ public abstract class ResultParser {
|
||||||
new ISBNResultParser(),
|
new ISBNResultParser(),
|
||||||
new ProductResultParser(),
|
new ProductResultParser(),
|
||||||
new ExpandedProductResultParser(),
|
new ExpandedProductResultParser(),
|
||||||
|
new VINResultParser(),
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final Pattern DIGITS = Pattern.compile("\\d*");
|
private static final Pattern DIGITS = Pattern.compile("\\d*");
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 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;
|
||||||
|
|
||||||
|
|
||||||
|
public final class VINParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
private final String vin;
|
||||||
|
private final String worldManufacturerID;
|
||||||
|
private final String vehicleDescriptorSection;
|
||||||
|
private final String vehicleIdentifierSection;
|
||||||
|
private final String countryCode;
|
||||||
|
private final String vehicleAttributes;
|
||||||
|
private final int modelYear;
|
||||||
|
private final char plantCode;
|
||||||
|
private final String sequentialNumber;
|
||||||
|
|
||||||
|
public VINParsedResult(String vin,
|
||||||
|
String worldManufacturerID,
|
||||||
|
String vehicleDescriptorSection,
|
||||||
|
String vehicleIdentifierSection,
|
||||||
|
String countryCode,
|
||||||
|
String vehicleAttributes,
|
||||||
|
int modelYear,
|
||||||
|
char plantCode,
|
||||||
|
String sequentialNumber) {
|
||||||
|
super(ParsedResultType.VIN);
|
||||||
|
this.vin = vin;
|
||||||
|
this.worldManufacturerID = worldManufacturerID;
|
||||||
|
this.vehicleDescriptorSection = vehicleDescriptorSection;
|
||||||
|
this.vehicleIdentifierSection = vehicleIdentifierSection;
|
||||||
|
this.countryCode = countryCode;
|
||||||
|
this.vehicleAttributes = vehicleAttributes;
|
||||||
|
this.modelYear = modelYear;
|
||||||
|
this.plantCode = plantCode;
|
||||||
|
this.sequentialNumber = sequentialNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVIN() {
|
||||||
|
return vin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWorldManufacturerID() {
|
||||||
|
return worldManufacturerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVehicleDescriptorSection() {
|
||||||
|
return vehicleDescriptorSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVehicleIdentifierSection() {
|
||||||
|
return vehicleIdentifierSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCountryCode() {
|
||||||
|
return countryCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVehicleAttributes() {
|
||||||
|
return vehicleAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getModelYear() {
|
||||||
|
return modelYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getPlantCode() {
|
||||||
|
return plantCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSequentialNumber() {
|
||||||
|
return sequentialNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayResult() {
|
||||||
|
StringBuilder result = new StringBuilder(50);
|
||||||
|
result.append(worldManufacturerID).append(' ');
|
||||||
|
result.append(vehicleDescriptorSection).append(' ');
|
||||||
|
result.append(vehicleIdentifierSection).append('\n');
|
||||||
|
if (countryCode != null) {
|
||||||
|
result.append(countryCode).append(' ');
|
||||||
|
}
|
||||||
|
result.append(modelYear).append(' ');
|
||||||
|
result.append(plantCode).append(' ');
|
||||||
|
result.append(sequentialNumber).append('\n');
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 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.BarcodeFormat;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects a result that is likely a vehicle identification number.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class VINResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private static final Pattern IOQ = Pattern.compile("[IOQ]");
|
||||||
|
private static final Pattern AZ09 = Pattern.compile("[A-Z0-9]{17}");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VINParsedResult parse(Result result) {
|
||||||
|
if (result.getBarcodeFormat() != BarcodeFormat.CODE_39) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String rawText = result.getText();
|
||||||
|
rawText = IOQ.matcher(rawText).replaceAll("").trim();
|
||||||
|
if (!AZ09.matcher(rawText).matches()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!checkChecksum(rawText)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String wmi = rawText.substring(0, 3);
|
||||||
|
return new VINParsedResult(rawText,
|
||||||
|
wmi,
|
||||||
|
rawText.substring(3, 9),
|
||||||
|
rawText.substring(9, 17),
|
||||||
|
countryCode(wmi),
|
||||||
|
rawText.substring(3, 8),
|
||||||
|
modelYear(rawText.charAt(9)),
|
||||||
|
rawText.charAt(10),
|
||||||
|
rawText.substring(11));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkChecksum(CharSequence vin) {
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < vin.length(); i++) {
|
||||||
|
sum += vinPositionWeight(i + 1) * vinCharValue(vin.charAt(i));
|
||||||
|
}
|
||||||
|
char checkChar = vin.charAt(8);
|
||||||
|
char expectedCheckChar = checkChar(sum % 11);
|
||||||
|
return checkChar == expectedCheckChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int vinCharValue(char c) {
|
||||||
|
if (c >= 'A' && c <= 'I') {
|
||||||
|
return (c - 'A') + 1;
|
||||||
|
}
|
||||||
|
if (c >= 'J' && c <= 'R') {
|
||||||
|
return (c - 'J') + 1;
|
||||||
|
}
|
||||||
|
if (c >= 'S' && c <= 'Z') {
|
||||||
|
return (c - 'S') + 2;
|
||||||
|
}
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
return c - '0';
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int vinPositionWeight(int position) {
|
||||||
|
if (position >= 1 && position <= 7) {
|
||||||
|
return 9 - position;
|
||||||
|
}
|
||||||
|
if (position == 8) {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
if (position == 9) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (position >= 10 && position <= 17) {
|
||||||
|
return 19 - position;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static char checkChar(int remainder) {
|
||||||
|
if (remainder < 10) {
|
||||||
|
return (char) ('0' + remainder);
|
||||||
|
}
|
||||||
|
if (remainder == 10) {
|
||||||
|
return 'X';
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int modelYear(char c) {
|
||||||
|
if (c >= 'E' && c <= 'H') {
|
||||||
|
return (c - 'E') + 1984;
|
||||||
|
}
|
||||||
|
if (c >= 'J' && c <= 'N') {
|
||||||
|
return (c - 'J') + 1988;
|
||||||
|
}
|
||||||
|
if (c == 'P') {
|
||||||
|
return 1993;
|
||||||
|
}
|
||||||
|
if (c >= 'R' && c <= 'T') {
|
||||||
|
return (c - 'R') + 1994;
|
||||||
|
}
|
||||||
|
if (c >= 'V' && c <= 'Y') {
|
||||||
|
return (c - 'V') + 1997;
|
||||||
|
}
|
||||||
|
if (c >= '1' && c <= '9') {
|
||||||
|
return (c - '1') + 2001;
|
||||||
|
}
|
||||||
|
if (c >= 'A' && c <= 'D') {
|
||||||
|
return (c - 'A') + 2010;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String countryCode(CharSequence wmi) {
|
||||||
|
char c1 = wmi.charAt(0);
|
||||||
|
char c2 = wmi.charAt(1);
|
||||||
|
switch (c1) {
|
||||||
|
case '1':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
return "US";
|
||||||
|
case '2':
|
||||||
|
return "CA";
|
||||||
|
case '3':
|
||||||
|
if (c2 >= 'A' && c2 <= 'W') {
|
||||||
|
return "MX";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '9':
|
||||||
|
if ((c2 >= 'A' && c2 <= 'E') || (c2 >= '3' && c2 <= '9')) {
|
||||||
|
return "BR";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'J':
|
||||||
|
if (c2 >= 'A' && c2 <= 'T') {
|
||||||
|
return "JP";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'K':
|
||||||
|
if (c2 >= 'L' && c2 <= 'R') {
|
||||||
|
return "KO";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
return "CN";
|
||||||
|
case 'M':
|
||||||
|
if (c2 >= 'A' && c2 <= 'E') {
|
||||||
|
return "IN";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
if (c2 >= 'A' && c2 <= 'M') {
|
||||||
|
return "UK";
|
||||||
|
}
|
||||||
|
if (c2 >= 'N' && c2 <= 'T') {
|
||||||
|
return "DE";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
if (c2 >= 'F' && c2 <= 'R') {
|
||||||
|
return "FR";
|
||||||
|
}
|
||||||
|
if (c2 >= 'S' && c2 <= 'W') {
|
||||||
|
return "ES";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
return "DE";
|
||||||
|
case 'X':
|
||||||
|
if (c2 == '0' || (c2 >= '3' && c2 <= '9')) {
|
||||||
|
return "RU";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
if (c2 >= 'A' && c2 <= 'R') {
|
||||||
|
return "IT";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 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.BarcodeFormat;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public final class VINParsedResultTestCase extends Assert {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotVIN() {
|
||||||
|
Result fakeResult = new Result("1M8GDM9A1KP042788", null, null, BarcodeFormat.CODE_39);
|
||||||
|
ParsedResult result = ResultParser.parseResult(fakeResult);
|
||||||
|
assertEquals(ParsedResultType.TEXT, result.getType());
|
||||||
|
fakeResult = new Result("1M8GDM9AXKP042788", null, null, BarcodeFormat.CODE_128);
|
||||||
|
result = ResultParser.parseResult(fakeResult);
|
||||||
|
assertEquals(ParsedResultType.TEXT, result.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVIN() {
|
||||||
|
doTest("1M8GDM9AXKP042788", "1M8", "GDM9AX", "KP042788", "US", "GDM9A", 1989, 'P', "042788");
|
||||||
|
doTest("I1M8GDM9AXKP042788", "1M8", "GDM9AX", "KP042788", "US", "GDM9A", 1989, 'P', "042788");
|
||||||
|
doTest("LJCPCBLCX11000237", "LJC", "PCBLCX", "11000237", "CN", "PCBLC", 2001, '1', "000237");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void doTest(String contents,
|
||||||
|
String wmi,
|
||||||
|
String vds,
|
||||||
|
String vis,
|
||||||
|
String country,
|
||||||
|
String attributes,
|
||||||
|
int year,
|
||||||
|
char plant,
|
||||||
|
String sequential) {
|
||||||
|
Result fakeResult = new Result(contents, null, null, BarcodeFormat.CODE_39);
|
||||||
|
ParsedResult result = ResultParser.parseResult(fakeResult);
|
||||||
|
assertSame(ParsedResultType.VIN, result.getType());
|
||||||
|
VINParsedResult vinResult = (VINParsedResult) result;
|
||||||
|
assertEquals(wmi, vinResult.getWorldManufacturerID());
|
||||||
|
assertEquals(vds, vinResult.getVehicleDescriptorSection());
|
||||||
|
assertEquals(vis, vinResult.getVehicleIdentifierSection());
|
||||||
|
assertEquals(country, vinResult.getCountryCode());
|
||||||
|
assertEquals(attributes, vinResult.getVehicleAttributes());
|
||||||
|
assertEquals(year, vinResult.getModelYear());
|
||||||
|
assertEquals(plant, vinResult.getPlantCode());
|
||||||
|
assertEquals(sequential, vinResult.getSequentialNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue