Editing
CommandLineOptions tutorial
Jump to navigation
Jump to search
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
===Processing command line options is easy, so why do I need a helper?=== To illustrate the reason being, let's develop a command line application to show the typical problems and how <code>CommandLineOptions</code> can help you: <pre> package com.acme.clo_demo; public class Diff { public static void main(String[] args) { boolean brief = false; boolean reportIdenticalFiles = false; int context = 3; int idx = 0; while (idx < args.length) { String arg = args[idx]; if (!arg.startsWith("-")) break; idx++; if ("-q".equals(arg) || "--brief".equals(arg)) { brief = true; } else if ("-s".equals(arg) || "--report-identical-files".equals(arg)) { reportIdenticalFiles = true; } else if ("-C".equals(arg) || "--Context".equals(arg)) { context = Integer.parseInt(args[idx++]); } else { System.err.println("Unrecognized command line option \"" + arg + "\""); } } // Now process args[idx]... and do the "real work". } } </pre> This implementation supports both "short" and "long" options, and detects invalid options. However there is room for improvement: * You cannot have "compact" short options, e.g. "<tt>-qs</tt>" for "<tt>-q -s</tt>". * You get an <code>ArrayIndexOutOfBoundsException</code> when "<tt>-C</tt>" is not followed by an argument. * You get a <code>NumberFormatException</code> when the argument of "<tt>-C</tt>" cannot be converted to an integer. * If the first argument after the options starts with a hyphen, it is identified as an invalid option. * The code is not documented. * The only support the user gets when it uses the tool is the "Unsupported command line option..." error message. So here's the second version that overcomes these deficits: <pre> package com.acme.clo_demo; /** * A (rudimentary) re-implementation of the well-known DIFF command line utility. */ public class Diff { /** * Parses the command line options and arguments, then prints the differences between two files or two * directories. * <p> * Supports the following command line options: * </p> * <dl> * <dt>--help</dt> * <dd>Print usage help and exit</dd> * <dt>-q, --brief</dt> * <dd>Don't report the differences, only which files have changed</dd> * <dt>-s, --report-identical-files</dt> * <dd>Also report about files that did NOT change</dd> * <dt>-C <var>NUM</var>, --context <var>NUM</var></dt> * <dd>Report differences in "context diff" format, with <var>NUM</var> lines of context</dd> * </dl> */ public static void main(String[] args) { boolean brief = false; boolean reportIdenticalFiles = false; int context = 3; int idx = 0; while (idx < args.length) { String arg = args[idx]; if (!arg.startsWith("-")) break; idx++; if ("--".equals(arg)) { break; } else if ("--help".equals(arg)) { System.out.println("Prints the differences between files."); System.out.println(); System.out.println("Usage:"); System.out.println(" diff [ <option> ] ... <file1> <file2>"); System.out.println(" Prints the differences between <file1> and <file2>."); System.out.println(" diff [ <option> ] ... <dir1> <dir2>"); System.out.println(" Prints the differences between the files under <dir1> and <dir2>."); System.out.println(); System.out.println("Supports the following command line options:"); System.out.println(" --help</dt>"); System.out.println(" Print usage help and exit"); System.out.println(" -q, --brief"); System.out.println(" Don't report the differences, only which files have changed"); System.out.println(" -s, --report-identical-files"); System.out.println(" Also report about files that did NOT change"); System.out.println(" -C <NUM>, --context <NUM>"); System.out.println(" Report differences in \"context diff\" format, with <NUM> lines of"); System.out.println(" context"); System.exit(0); } else if ("-q".equals(arg) || "--brief".equals(arg)) { brief = true; } else if ("-s".equals(arg) || "--report-identical-files".equals(arg)) { reportIdenticalFiles = true; } else if ("-C".equals(arg) || "--Context".equals(arg)) { if (idx == args.length) { System.err.println("Count missing after \"-C\" or \"--context\"."); System.exit(1); } try { context = Integer.parseInt(args[idx++]); } catch (NumberFormatException nfe) { System.err.println("Cannot convert \"" + args[idx - 1] + "\" to an integer"); System.exit(1); } } else { System.err.println("Unrecognized command line option \"" + arg + "\"; try \"--help\"."); } } // Now process args[idx]... and do the "real work". } } </pre> Well, that's a lot of boilerplate code! And it's just this trivial example... Also, people may want to read about the tool without installing or running it, so you fire up the word processor, or the HTML editor, or the Wiki... But after all, you have written and documented a professional command line tool, congratulations! But wait, now there is number of ''new'' problems: * The code for processing the command line options is ''huge'', and most of it deals with trivial error conditions. * There is a lot of redundancy in the command line options: There's the CLO ''implementation'', and the ''description'' appears in three places: The JAVADOC of the <code>main()</code> method, the <code>System.out.println()</code> for the "<tt>--help</tt>" option, and in the online/paper documentation. What a maintenance nightmare! ==So how can <code>CommandLineOptions</code> help?== Here's how it works: '''First''', you replace the command line option parsing with a call to [http://commons.unkrig.de/javadoc/de/unkrig/commons/util/CommandLineOptions.html#parse-java.lang.String:A-java.lang.Object- CommandLineOptions.parse()]. Also add a method for each command line option, annotate it with [http://commons.unkrig.de/javadoc/de/unkrig/commons/util/annotation/CommandLineOption.html @CommandLineOption] and add its description as JAVADOC. Put the usage text into the JAVADOC comment of the "<code>main()</code>" method, with a placeholder "<tt>{@command-line-options}</tt> for the descriptions of the command line options. '''Second''', you generate HTML documentation with the [[Doclet#The_MAIN_Doclet|MAIN doclet]]: <javadoc doclet="de.unkrig.doclet.main.MainDoclet" docletpath="de.unkrig.doclet.main.jar" destdir="src"> <fileset file="src/com/acme/clo_demo/Diff.java" /> </javadoc> '''Third''', you convert the HTML documentation into plain text with the [http://html2txt.unkrig.de HTML2TXT] utility: <taskdef classpath="html2txt.jar" resource="de/unkrig/html2txt/antlib.xml" /> <html2txt> <fileset dir="src" includes="com/acme/clo_demo/Diff.main(String[]).html" /> </html2txt> '''Fourth''', rewrite the "<tt>--help</tt>" option to simply copy the plain text documentation to <code>System.out</code>. ==So here's the code== <pre> package com.acme.clo_demo; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.regex.Pattern; import de.unkrig.commons.text.pattern.PatternUtil; import de.unkrig.commons.util.CommandLineOptions; import de.unkrig.commons.util.annotation.CommandLineOption; /** * A (rudimentary) re-implementation of the well-known DIFF command line utility. */ public class Diff { /** * Prints the differences between files. * <h2>Usage:</h2> * <dl> * <dt>diff [ <var>option</var> ] ... <var>file1</var> <var>file2</var></dt> * <dd>Prints the differences between <var>file1</var> and <var>file2</var>.</dd> * <dt>diff [ <var>option</var> ] ... <var>dir1</var> <var>dir2</var></dt> * <dd>Prints the differences between the files under <var>dir1</var> and <var>dir2</var>.</dd> * </dl> * <h2>Options:</h2> * {@command-line-options} */ public static void main(String[] args) { Diff diff = new Diff(); args = CommandLineOptions.parse(args, diff); // Now process args[idx]... and do the "real work". } private boolean brief = false; private boolean reportIdenticalFiles = false; private int context = 3; /** * Print usage help and exit. */ @CommandLineOption public void help() throws IOException { CommandLineOptions.printResource(Diff.class, "main(String[]).txt", null, System.out); System.exit(0); } /** * Don't report the differences, only which files have changed. */ @CommandLineOption(name = { "-q", "--brief" }) public void setBrief() { this.brief = true; } /** * Also report about files that did NOT change. */ @CommandLineOption(name = { "-s", "--report-identical-files"}) public void setReportIdenticalFiles() { this.reportIdenticalFiles = true; } /** * Report differences in "context diff" format, with <var>number-of-lines</var> lines of context. */ @CommandLineOption(name = { "-C", "--Context" }) public void setContext(int numberOfLines) { this.context = numberOfLines; } } </pre> When you run it with the "<tt>--help</tt>" option, you get: Prints the differences between files. Usage: ====== diff [ <option> ] ... <file1> <file2> Prints the differences between <file1> and <file2>. diff [ <option> ] ... <dir1> <dir2> Prints the differences between the files under <dir1> and <dir2>. Options: ======== --help Print usage help and exit. -q --brief Don't report the differences, only which files have changed. -s --report-identical-files Also report about files that did NOT change. -C <number-of-lines> --context <number-of-lines> Report differences in "context diff" format, with <number-of-lines> lines of context. And voila - you have zero redundancy, and HTML documentation for printing and/or online presentation for free!
Summary:
Please note that all contributions to unkrig.de may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
Unkrig.de:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Navigation menu
Personal tools
Not logged in
Talk
Contributions
Create account
Log in
Namespaces
Page
Discussion
English
Views
Read
Edit
View history
More
Search
Navigation
Main page
Recent changes
Random page
Help
Tools
What links here
Related changes
Special pages
Page information