Skip to content

Commit

Permalink
Use JCommander for CommandLineRunner args
Browse files Browse the repository at this point in the history
  • Loading branch information
srowen committed May 3, 2015
1 parent 106fd2f commit 8fe986a
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 278 deletions.
6 changes: 6 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ Copyright 2005-2006 Dietmar Bürkle
Portions of this software were contributed under section 5 of the
Apache License. Contributors are listed under:
http://barcode4j.sourceforge.net/contributors.html

--------------------------------------------------------------------------------
NOTICES FOR JCOMMANDER
--------------------------------------------------------------------------------

Copyright 2010 Cedric Beust [email protected]
5 changes: 5 additions & 0 deletions javase/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
</dependency>
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
<version>1.48</version>
</dependency>
</dependencies>

<parent>
Expand Down
204 changes: 61 additions & 143 deletions javase/src/main/java/com/google/zxing/client/j2se/CommandLineRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,22 @@

package com.google.zxing.client.j2se;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;

import com.beust.jcommander.JCommander;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Pattern;

/**
* This simple command line utility decodes files, directories of files, or URIs which are passed
Expand All @@ -47,188 +44,109 @@
*/
public final class CommandLineRunner {

private static final Pattern COMMA = Pattern.compile(",");

private CommandLineRunner() {
}

public static void main(String[] args) throws Exception {
if (args.length == 0) {
printUsage();
DecoderConfig config = new DecoderConfig();
JCommander jCommander = new JCommander(config, args);
jCommander.setProgramName(CommandLineRunner.class.getSimpleName());
if (config.help) {
jCommander.usage();
return;
}

Config config = new Config();
Queue<URI> inputs = new ConcurrentLinkedQueue<>();

for (String arg : args) {
String[] argValue = arg.split("=");
switch (argValue[0]) {
case "--try_harder":
config.setTryHarder(true);
break;
case "--pure_barcode":
config.setPureBarcode(true);
break;
case "--products_only":
config.setProductsOnly(true);
break;
case "--dump_results":
config.setDumpResults(true);
break;
case "--dump_black_point":
config.setDumpBlackPoint(true);
break;
case "--multi":
config.setMulti(true);
break;
case "--brief":
config.setBrief(true);
break;
case "--recursive":
config.setRecursive(true);
break;
case "--crop":
int[] crop = new int[4];
String[] tokens = COMMA.split(argValue[1]);
for (int i = 0; i < crop.length; i++) {
crop[i] = Integer.parseInt(tokens[i]);
}
config.setCrop(crop);
break;
case "--possibleFormats":
config.setPossibleFormats(COMMA.split(argValue[1]));
break;
default:
if (arg.startsWith("-")) {
System.err.println("Unknown command line option " + arg);
printUsage();
return;
}
URI argURI = URI.create(arg);
if (argURI.getScheme() == null) {
argURI = new URI("file", argURI.getSchemeSpecificPart(), argURI.getFragment());
}
addArgumentToInputs(argURI, config, inputs);
break;
}
}
List<URI> inputs = config.inputPaths;
do {
inputs = retainValid(expand(inputs), config.recursive);
} while (config.recursive && isExpandable(inputs));

int numInputs = inputs.size();
if (numInputs == 0) {
System.err.println("No inputs specified");
printUsage();
jCommander.usage();
return;
}

config.setHints(buildHints(config));

Queue<URI> syncInputs = new ConcurrentLinkedQueue<>(inputs);
int numThreads = Math.min(numInputs, Runtime.getRuntime().availableProcessors());
int successful = 0;
if (numThreads > 1) {
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
Collection<Future<Integer>> futures = new ArrayList<>(numThreads);
for (int x = 0; x < numThreads; x++) {
futures.add(executor.submit(new DecodeWorker(config, inputs)));
futures.add(executor.submit(new DecodeWorker(config, syncInputs)));
}
executor.shutdown();
for (Future<Integer> future : futures) {
successful += future.get();
}
} else {
successful += new DecodeWorker(config, inputs).call();
successful += new DecodeWorker(config, syncInputs).call();
}

if (numInputs > 1) {
if (!config.brief && numInputs > 1) {
System.out.println("\nDecoded " + successful + " files out of " + numInputs +
" successfully (" + (successful * 100 / numInputs) + "%)\n");
}
}

/**
* Build all the inputs up front into a single flat list, so the threads can atomically pull
* paths/URLs off the queue.
*/
private static void addArgumentToInputs(URI input, Config config, Queue<URI> inputs) throws IOException {
// Special case: a local directory
if ("file".equals(input.getScheme()) && Files.isDirectory(Paths.get(input))) {
try (DirectoryStream<Path> childPaths = Files.newDirectoryStream(Paths.get(input))) {
for (Path childPath : childPaths) {
Path realChildPath = childPath.toRealPath();
// Skip hidden files and directories (e.g. svn stuff).
if (!realChildPath.getFileName().toString().startsWith(".")) {
// Recur on nested directories if requested, otherwise skip them.
if (config.isRecursive() && Files.isDirectory(realChildPath)) {
addArgumentToInputs(realChildPath.toUri(), config, inputs);
} else {
inputs.add(realChildPath.toUri());
private static List<URI> expand(List<URI> inputs) throws IOException, URISyntaxException {
List<URI> expanded = new ArrayList<>();
for (URI input : inputs) {
if (isFileOrDir(input)) {
Path inputPath = Paths.get(input);
if (Files.isDirectory(inputPath)) {
try (DirectoryStream<Path> childPaths = Files.newDirectoryStream(inputPath)) {
for (Path childPath : childPaths) {
expanded.add(childPath.toUri());
}
}
} else {
expanded.add(input);
}
} else {
expanded.add(input);
}
}
for (int i = 0; i < expanded.size(); i++) {
URI input = expanded.get(i);
if (input.getScheme() == null) {
expanded.set(i, new URI("file", input.getSchemeSpecificPart(), input.getFragment()));
}
} else {
inputs.add(input);
}
return expanded;
}

private static Map<DecodeHintType,?> buildHints(Config config) {
Collection<BarcodeFormat> possibleFormats = new ArrayList<>();
String[] possibleFormatsNames = config.getPossibleFormats();
if (possibleFormatsNames != null && possibleFormatsNames.length > 0) {
for (String format : possibleFormatsNames) {
possibleFormats.add(BarcodeFormat.valueOf(format));
private static List<URI> retainValid(List<URI> inputs, boolean recursive) {
List<URI> retained = new ArrayList<>();
for (URI input : inputs) {
boolean retain;
if (isFileOrDir(input)) {
Path inputPath = Paths.get(input);
retain =
!inputPath.getFileName().toString().startsWith(".") &&
(recursive || !Files.isDirectory(inputPath));
} else {
retain = true;
}
} else {
possibleFormats.add(BarcodeFormat.UPC_A);
possibleFormats.add(BarcodeFormat.UPC_E);
possibleFormats.add(BarcodeFormat.EAN_13);
possibleFormats.add(BarcodeFormat.EAN_8);
possibleFormats.add(BarcodeFormat.RSS_14);
possibleFormats.add(BarcodeFormat.RSS_EXPANDED);
if (!config.isProductsOnly()) {
possibleFormats.add(BarcodeFormat.CODE_39);
possibleFormats.add(BarcodeFormat.CODE_93);
possibleFormats.add(BarcodeFormat.CODE_128);
possibleFormats.add(BarcodeFormat.ITF);
possibleFormats.add(BarcodeFormat.QR_CODE);
possibleFormats.add(BarcodeFormat.DATA_MATRIX);
possibleFormats.add(BarcodeFormat.AZTEC);
possibleFormats.add(BarcodeFormat.PDF_417);
possibleFormats.add(BarcodeFormat.CODABAR);
possibleFormats.add(BarcodeFormat.MAXICODE);
if (retain) {
retained.add(input);
}
}
Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
hints.put(DecodeHintType.POSSIBLE_FORMATS, possibleFormats);
if (config.isTryHarder()) {
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
}
if (config.isPureBarcode()) {
hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
}
return hints;
return retained;
}

private static void printUsage() {
System.err.println("Decode barcode images using the ZXing library");
System.err.println();
System.err.println("usage: CommandLineRunner { file | dir | url } [ options ]");
System.err.println(" --try_harder: Use the TRY_HARDER hint, default is normal (mobile) mode");
System.err.println(" --pure_barcode: Input image is a pure monochrome barcode image, not a photo");
System.err.println(" --products_only: Only decode the UPC and EAN families of barcodes");
System.err.println(" --dump_results: Write the decoded contents to input.txt");
System.err.println(" --dump_black_point: Compare black point algorithms as input.mono.png");
System.err.println(" --multi: Scans image for multiple barcodes");
System.err.println(" --brief: Only output one line per file, omitting the contents");
System.err.println(" --recursive: Descend into subdirectories");
System.err.println(" --crop=left,top,width,height: Only examine cropped region of input image(s)");
StringBuilder builder = new StringBuilder();
builder.append(" --possibleFormats=barcodeFormat[,barcodeFormat2...] where barcodeFormat is any of: ");
for (BarcodeFormat format : BarcodeFormat.values()) {
builder.append(format).append(',');
private static boolean isExpandable(List<URI> inputs) {
for (URI input : inputs) {
if (isFileOrDir(input) && Files.isDirectory(Paths.get(input))) {
return true;
}
}
builder.setLength(builder.length() - 1);
System.err.println(builder);
return false;
}

private static boolean isFileOrDir(URI uri) {
return "file".equals(uri.getScheme());
}

}
Loading

0 comments on commit 8fe986a

Please sign in to comment.