mirror of
https://github.com/jlengrand/picocli.git
synced 2026-03-10 15:51:44 +00:00
1056 lines
67 KiB
HTML
1056 lines
67 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="generator" content="Asciidoctor 2.0.10">
|
||
<meta name="author" content="Remko Popma">
|
||
<title>Migrating from Commons CLI to picocli</title>
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
|
||
<style>
|
||
/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
|
||
/* Uncomment @import statement to use as custom stylesheet */
|
||
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
|
||
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}
|
||
audio,video{display:inline-block}
|
||
audio:not([controls]){display:none;height:0}
|
||
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
|
||
a{background:none}
|
||
a:focus{outline:thin dotted}
|
||
a:active,a:hover{outline:0}
|
||
h1{font-size:2em;margin:.67em 0}
|
||
abbr[title]{border-bottom:1px dotted}
|
||
b,strong{font-weight:bold}
|
||
dfn{font-style:italic}
|
||
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
|
||
mark{background:#ff0;color:#000}
|
||
code,kbd,pre,samp{font-family:monospace;font-size:1em}
|
||
pre{white-space:pre-wrap}
|
||
q{quotes:"\201C" "\201D" "\2018" "\2019"}
|
||
small{font-size:80%}
|
||
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||
sup{top:-.5em}
|
||
sub{bottom:-.25em}
|
||
img{border:0}
|
||
svg:not(:root){overflow:hidden}
|
||
figure{margin:0}
|
||
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
|
||
legend{border:0;padding:0}
|
||
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
|
||
button,input{line-height:normal}
|
||
button,select{text-transform:none}
|
||
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
|
||
button[disabled],html input[disabled]{cursor:default}
|
||
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
|
||
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
|
||
textarea{overflow:auto;vertical-align:top}
|
||
table{border-collapse:collapse;border-spacing:0}
|
||
*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
|
||
html,body{font-size:100%}
|
||
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
|
||
a:hover{cursor:pointer}
|
||
img,object,embed{max-width:100%;height:auto}
|
||
object,embed{height:100%}
|
||
img{-ms-interpolation-mode:bicubic}
|
||
.left{float:left!important}
|
||
.right{float:right!important}
|
||
.text-left{text-align:left!important}
|
||
.text-right{text-align:right!important}
|
||
.text-center{text-align:center!important}
|
||
.text-justify{text-align:justify!important}
|
||
.hide{display:none}
|
||
img,object,svg{display:inline-block;vertical-align:middle}
|
||
textarea{height:auto;min-height:50px}
|
||
select{width:100%}
|
||
.center{margin-left:auto;margin-right:auto}
|
||
.stretch{width:100%}
|
||
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
|
||
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
|
||
a{color:#2156a5;text-decoration:underline;line-height:inherit}
|
||
a:hover,a:focus{color:#1d4b8f}
|
||
a img{border:0}
|
||
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
|
||
p aside{font-size:.875em;line-height:1.35;font-style:italic}
|
||
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
|
||
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
|
||
h1{font-size:2.125em}
|
||
h2{font-size:1.6875em}
|
||
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
|
||
h4,h5{font-size:1.125em}
|
||
h6{font-size:1em}
|
||
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
|
||
em,i{font-style:italic;line-height:inherit}
|
||
strong,b{font-weight:bold;line-height:inherit}
|
||
small{font-size:60%;line-height:inherit}
|
||
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
|
||
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
|
||
ul,ol{margin-left:1.5em}
|
||
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
|
||
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
|
||
ul.square{list-style-type:square}
|
||
ul.circle{list-style-type:circle}
|
||
ul.disc{list-style-type:disc}
|
||
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
|
||
dl dt{margin-bottom:.3125em;font-weight:bold}
|
||
dl dd{margin-bottom:1.25em}
|
||
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
|
||
abbr{text-transform:none}
|
||
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
|
||
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
|
||
blockquote cite::before{content:"\2014 \0020"}
|
||
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
|
||
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
|
||
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
|
||
h1{font-size:2.75em}
|
||
h2{font-size:2.3125em}
|
||
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
|
||
h4{font-size:1.4375em}}
|
||
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
|
||
table thead,table tfoot{background:#f7f8f7}
|
||
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
|
||
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
|
||
table tr.even,table tr.alt{background:#f8f8f7}
|
||
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
|
||
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
|
||
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
|
||
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
|
||
.clearfix::after,.float-group::after{clear:both}
|
||
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}
|
||
:not(pre)>code.nobreak{word-wrap:normal}
|
||
:not(pre)>code.nowrap{white-space:nowrap}
|
||
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
|
||
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
|
||
pre>code{display:block}
|
||
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
|
||
em em{font-style:normal}
|
||
strong strong{font-weight:400}
|
||
.keyseq{color:rgba(51,51,51,.8)}
|
||
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
|
||
.keyseq kbd:first-child{margin-left:0}
|
||
.keyseq kbd:last-child{margin-right:0}
|
||
.menuseq,.menuref{color:#000}
|
||
.menuseq b:not(.caret),.menuref{font-weight:inherit}
|
||
.menuseq{word-spacing:-.02em}
|
||
.menuseq b.caret{font-size:1.25em;line-height:.8}
|
||
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
|
||
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
|
||
b.button::before{content:"[";padding:0 3px 0 2px}
|
||
b.button::after{content:"]";padding:0 2px 0 3px}
|
||
p a>code:hover{color:rgba(0,0,0,.9)}
|
||
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
|
||
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
|
||
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
|
||
#content{margin-top:1.25em}
|
||
#content::before{content:none}
|
||
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
|
||
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
|
||
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
|
||
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
|
||
#header .details span:first-child{margin-left:-.125em}
|
||
#header .details span.email a{color:rgba(0,0,0,.85)}
|
||
#header .details br{display:none}
|
||
#header .details br+span::before{content:"\00a0\2013\00a0"}
|
||
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
|
||
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
|
||
#header #revnumber{text-transform:capitalize}
|
||
#header #revnumber::after{content:"\00a0"}
|
||
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
|
||
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
|
||
#toc>ul{margin-left:.125em}
|
||
#toc ul.sectlevel0>li>a{font-style:italic}
|
||
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
|
||
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
|
||
#toc li{line-height:1.3334;margin-top:.3334em}
|
||
#toc a{text-decoration:none}
|
||
#toc a:active{text-decoration:underline}
|
||
#toctitle{color:#7a2518;font-size:1.2em}
|
||
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
|
||
body.toc2{padding-left:15em;padding-right:0}
|
||
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
|
||
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
|
||
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
|
||
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
|
||
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
|
||
body.toc2.toc-right{padding-left:0;padding-right:15em}
|
||
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
|
||
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
|
||
#toc.toc2{width:20em}
|
||
#toc.toc2 #toctitle{font-size:1.375em}
|
||
#toc.toc2>ul{font-size:.95em}
|
||
#toc.toc2 ul ul{padding-left:1.25em}
|
||
body.toc2.toc-right{padding-left:0;padding-right:20em}}
|
||
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
|
||
#content #toc>:first-child{margin-top:0}
|
||
#content #toc>:last-child{margin-bottom:0}
|
||
#footer{max-width:100%;background:rgba(0,0,0,.8);padding:1.25em}
|
||
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
|
||
#content{margin-bottom:.625em}
|
||
.sect1{padding-bottom:.625em}
|
||
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
|
||
.sect1{padding-bottom:1.25em}}
|
||
.sect1:last-child{padding-bottom:0}
|
||
.sect1+.sect1{border-top:1px solid #e7e7e9}
|
||
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
|
||
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
|
||
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
|
||
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
|
||
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
|
||
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
|
||
details>summary:first-of-type{cursor:pointer;display:list-item;outline:none;margin-bottom:.75em}
|
||
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
|
||
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
|
||
.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
|
||
table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit}
|
||
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
|
||
.admonitionblock>table td.icon{text-align:center;width:80px}
|
||
.admonitionblock>table td.icon img{max-width:none}
|
||
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
|
||
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6)}
|
||
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
|
||
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
|
||
.exampleblock>.content>:first-child{margin-top:0}
|
||
.exampleblock>.content>:last-child{margin-bottom:0}
|
||
.sidebarblock{border-style:solid;border-width:1px;border-color:#dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;-webkit-border-radius:4px;border-radius:4px}
|
||
.sidebarblock>:first-child{margin-top:0}
|
||
.sidebarblock>:last-child{margin-bottom:0}
|
||
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
|
||
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
|
||
.literalblock pre,.listingblock>.content>pre{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;overflow-x:auto;padding:1em;font-size:.8125em}
|
||
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
|
||
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
|
||
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class="highlight"],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
|
||
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
|
||
.listingblock>.content{position:relative}
|
||
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
|
||
.listingblock:hover code[data-lang]::before{display:block}
|
||
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
|
||
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
|
||
.listingblock pre.highlightjs{padding:0}
|
||
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
|
||
.listingblock pre.prettyprint{border-width:0}
|
||
.prettyprint{background:#f7f7f8}
|
||
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
|
||
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
|
||
pre.prettyprint li code[data-lang]::before{opacity:1}
|
||
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
|
||
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
|
||
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
|
||
table.linenotable td.code{padding-left:.75em}
|
||
table.linenotable td.linenos{border-right:1px solid currentColor;opacity:.35;padding-right:.5em}
|
||
pre.pygments .lineno{border-right:1px solid currentColor;opacity:.35;display:inline-block;margin-right:.75em}
|
||
pre.pygments .lineno::before{content:"";margin-right:-.125em}
|
||
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
|
||
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
|
||
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
|
||
.quoteblock blockquote{margin:0;padding:0;border:0}
|
||
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
|
||
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
|
||
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
|
||
.verseblock{margin:0 1em 1.25em}
|
||
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
|
||
.verseblock pre strong{font-weight:400}
|
||
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
|
||
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
|
||
.quoteblock .attribution br,.verseblock .attribution br{display:none}
|
||
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
|
||
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
|
||
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
|
||
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
|
||
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
|
||
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
|
||
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
|
||
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
|
||
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;text-align:left;margin-right:0}
|
||
table.tableblock{max-width:100%;border-collapse:separate}
|
||
p.tableblock:last-child{margin-bottom:0}
|
||
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
|
||
td.tableblock>.content>:last-child.sidebarblock{margin-bottom:0}
|
||
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
|
||
table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}
|
||
table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}
|
||
table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}
|
||
table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px}
|
||
table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0}
|
||
table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}
|
||
table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}
|
||
table.frame-all{border-width:1px}
|
||
table.frame-sides{border-width:0 1px}
|
||
table.frame-topbot,table.frame-ends{border-width:1px 0}
|
||
table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7}
|
||
th.halign-left,td.halign-left{text-align:left}
|
||
th.halign-right,td.halign-right{text-align:right}
|
||
th.halign-center,td.halign-center{text-align:center}
|
||
th.valign-top,td.valign-top{vertical-align:top}
|
||
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
|
||
th.valign-middle,td.valign-middle{vertical-align:middle}
|
||
table thead th,table tfoot th{font-weight:bold}
|
||
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
|
||
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
|
||
p.tableblock>code:only-child{background:none;padding:0}
|
||
p.tableblock{font-size:1em}
|
||
ol{margin-left:1.75em}
|
||
ul li ol{margin-left:1.5em}
|
||
dl dd{margin-left:1.125em}
|
||
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
|
||
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
|
||
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
|
||
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
|
||
ul.unstyled,ol.unstyled{margin-left:0}
|
||
ul.checklist{margin-left:.625em}
|
||
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
|
||
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
|
||
ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
|
||
ul.inline>li{margin-left:1.25em}
|
||
.unstyled dl dt{font-weight:400;font-style:normal}
|
||
ol.arabic{list-style-type:decimal}
|
||
ol.decimal{list-style-type:decimal-leading-zero}
|
||
ol.loweralpha{list-style-type:lower-alpha}
|
||
ol.upperalpha{list-style-type:upper-alpha}
|
||
ol.lowerroman{list-style-type:lower-roman}
|
||
ol.upperroman{list-style-type:upper-roman}
|
||
ol.lowergreek{list-style-type:lower-greek}
|
||
.hdlist>table,.colist>table{border:0;background:none}
|
||
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
|
||
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
|
||
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
|
||
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
|
||
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
|
||
.colist td:not([class]):first-child img{max-width:none}
|
||
.colist td:not([class]):last-child{padding:.25em 0}
|
||
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
|
||
.imageblock.left{margin:.25em .625em 1.25em 0}
|
||
.imageblock.right{margin:.25em 0 1.25em .625em}
|
||
.imageblock>.title{margin-bottom:0}
|
||
.imageblock.thumb,.imageblock.th{border-width:6px}
|
||
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
|
||
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
|
||
.image.left{margin-right:.625em}
|
||
.image.right{margin-left:.625em}
|
||
a.image{text-decoration:none;display:inline-block}
|
||
a.image object{pointer-events:none}
|
||
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
|
||
sup.footnote a,sup.footnoteref a{text-decoration:none}
|
||
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
|
||
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
|
||
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
|
||
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
|
||
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
|
||
#footnotes .footnote:last-of-type{margin-bottom:0}
|
||
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
|
||
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
|
||
.gist .file-data>table td.line-data{width:99%}
|
||
div.unbreakable{page-break-inside:avoid}
|
||
.big{font-size:larger}
|
||
.small{font-size:smaller}
|
||
.underline{text-decoration:underline}
|
||
.overline{text-decoration:overline}
|
||
.line-through{text-decoration:line-through}
|
||
.aqua{color:#00bfbf}
|
||
.aqua-background{background:#00fafa}
|
||
.black{color:#000}
|
||
.black-background{background:#000}
|
||
.blue{color:#0000bf}
|
||
.blue-background{background:#0000fa}
|
||
.fuchsia{color:#bf00bf}
|
||
.fuchsia-background{background:#fa00fa}
|
||
.gray{color:#606060}
|
||
.gray-background{background:#7d7d7d}
|
||
.green{color:#006000}
|
||
.green-background{background:#007d00}
|
||
.lime{color:#00bf00}
|
||
.lime-background{background:#00fa00}
|
||
.maroon{color:#600000}
|
||
.maroon-background{background:#7d0000}
|
||
.navy{color:#000060}
|
||
.navy-background{background:#00007d}
|
||
.olive{color:#606000}
|
||
.olive-background{background:#7d7d00}
|
||
.purple{color:#600060}
|
||
.purple-background{background:#7d007d}
|
||
.red{color:#bf0000}
|
||
.red-background{background:#fa0000}
|
||
.silver{color:#909090}
|
||
.silver-background{background:#bcbcbc}
|
||
.teal{color:#006060}
|
||
.teal-background{background:#007d7d}
|
||
.white{color:#bfbfbf}
|
||
.white-background{background:#fafafa}
|
||
.yellow{color:#bfbf00}
|
||
.yellow-background{background:#fafa00}
|
||
span.icon>.fa{cursor:default}
|
||
a span.icon>.fa{cursor:inherit}
|
||
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
|
||
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
|
||
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
|
||
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
|
||
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
|
||
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
|
||
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
|
||
.conum[data-value] *{color:#fff!important}
|
||
.conum[data-value]+b{display:none}
|
||
.conum[data-value]::after{content:attr(data-value)}
|
||
pre .conum[data-value]{position:relative;top:-.125em}
|
||
b.conum *{color:inherit!important}
|
||
.conum:not([data-value]):empty{display:none}
|
||
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
|
||
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
|
||
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
|
||
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
|
||
p{margin-bottom:1.25rem}
|
||
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
|
||
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
|
||
.print-only{display:none!important}
|
||
@page{margin:1.25cm .75cm}
|
||
@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
|
||
html{font-size:80%}
|
||
a{color:inherit!important;text-decoration:underline!important}
|
||
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
|
||
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
|
||
abbr[title]::after{content:" (" attr(title) ")"}
|
||
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
|
||
thead{display:table-header-group}
|
||
svg{max-width:100%}
|
||
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
|
||
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
|
||
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
|
||
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
|
||
body.book #header{text-align:center}
|
||
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
|
||
body.book #header .details{border:0!important;display:block;padding:0!important}
|
||
body.book #header .details span:first-child{margin-left:0!important}
|
||
body.book #header .details br{display:block}
|
||
body.book #header .details br+span::before{content:none!important}
|
||
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
|
||
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
|
||
.listingblock code[data-lang]::before{display:block}
|
||
#footer{padding:0 .9375em}
|
||
.hide-on-print{display:none!important}
|
||
.print-only{display:block!important}
|
||
.hide-for-print{display:none!important}
|
||
.show-for-print{display:inherit!important}}
|
||
@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem}
|
||
.sect1{padding:0!important}
|
||
.sect1+.sect1{border:0}
|
||
#footer{background:none}
|
||
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
|
||
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
|
||
</style>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||
<style>
|
||
.hidden {
|
||
display: none;
|
||
}
|
||
|
||
.switch {
|
||
border-width: 1px 1px 0 1px;
|
||
border-style: solid;
|
||
border-color: #7a2518;
|
||
display: inline-block;
|
||
}
|
||
|
||
.switch--item {
|
||
padding: 10px;
|
||
background-color: #ffffff;
|
||
color: #7a2518;
|
||
display: inline-block;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.switch--item.selected {
|
||
background-color: #7a2519;
|
||
color: #ffffff;
|
||
}
|
||
|
||
</style>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
|
||
<script type="text/javascript">
|
||
function addBlockSwitches() {
|
||
$('.primary').each(function() {
|
||
primary = $(this);
|
||
createSwitchItem(primary, createBlockSwitch(primary)).item.addClass("selected");
|
||
primary.children('.title').remove();
|
||
});
|
||
$('.secondary').each(function(idx, node) {
|
||
secondary = $(node);
|
||
primary = findPrimary(secondary);
|
||
switchItem = createSwitchItem(secondary, primary.children('.switch'));
|
||
switchItem.content.addClass('hidden');
|
||
findPrimary(secondary).append(switchItem.content);
|
||
secondary.remove();
|
||
});
|
||
}
|
||
|
||
function createBlockSwitch(primary) {
|
||
blockSwitch = $('<div class="switch"></div>');
|
||
primary.prepend(blockSwitch);
|
||
return blockSwitch;
|
||
}
|
||
|
||
function findPrimary(secondary) {
|
||
candidate = secondary.prev();
|
||
while (!candidate.is('.primary')) {
|
||
candidate = candidate.prev();
|
||
}
|
||
return candidate;
|
||
}
|
||
|
||
function createSwitchItem(block, blockSwitch) {
|
||
blockName = block.children('.title').text();
|
||
content = block.children('.content').first().append(block.next('.colist'));
|
||
item = $('<div class="switch--item">' + blockName + '</div>');
|
||
item.on('click', '', content, function(e) {
|
||
$(this).addClass('selected');
|
||
$(this).siblings().removeClass('selected');
|
||
e.data.siblings('.content').addClass('hidden');
|
||
e.data.removeClass('hidden');
|
||
});
|
||
blockSwitch.append(item);
|
||
return {'item': item, 'content': content};
|
||
}
|
||
|
||
$(addBlockSwitches);
|
||
|
||
</script>
|
||
|
||
</head>
|
||
<body class="article">
|
||
<div id="header">
|
||
<h1>Migrating from Commons CLI to picocli</h1>
|
||
<div class="details">
|
||
<span id="author" class="author">Remko Popma</span><br>
|
||
<span id="revnumber">version 4.5.2,</span>
|
||
<span id="revdate">2018-11-19</span>
|
||
</div>
|
||
</div>
|
||
<div id="content">
|
||
<div id="preamble">
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Apache Commons CLI, initially released in 2002, is perhaps the most widely used java command line parser, but its API shows its age.
|
||
Applications looking for a modern approach with a minimum of boilerplate code may be interested in <a href="https://github.com/remkop/picocli">picocli</a>. Why is it worth the trouble to migrate, and how do you migrate your Commons CLI-based application to picocli? Picocli offers a fluent API with strong typing, usage help with ANSI colors, autocompletion and a host of other features. Let’s take a look using <a href="http://checkstyle.sourceforge.net">Checkstyle</a> as an example.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_why_migrate">Why Migrate?</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Is migrating from Commons CLI to picocli worth the trouble? What is the benefit of moving from one command line parser to another? Is this more than just redecorating the living room of our application?</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_end_user_experience">End User Experience</h3>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/pexels-photo-1210532.jpeg" alt="pexels photo 1210532" width="200"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>What are the benefits for end users?</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p><strong>Command line completion</strong>. Picocli-based applications can have <a href="https://picocli.info/autocomplete.html">command line completion</a> in bash and zsh shells, as well as in JLine-based <a href="https://github.com/remkop/picocli/tree/master/picocli-shell-jline2">interactive shell</a> applications.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Beautiful, highly readable <strong>usage help</strong> messages. The usage help generated by Commons CLI is a bit minimalistic. Out of the box, picocli generates help that uses <a href="https://picocli.info/#_ansi_colors_and_styles">ANSI styles and colors</a> for contrast to emphasize important information like commands, options, and parameters. The help message layout is easy to customize using the annotations. Additionally, there is a Help API in case you want something different. See the picocli <a href="https://github.com/remkop/picocli">README</a> for some example screenshots.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Support for <strong>very large command lines</strong> via @-files, or <a href="https://picocli.info/#AtFiles">"argument files"</a>. Sometimes users need to specify command lines that are longer than supported by the operating system or the shell. When picocli encounters an argument beginning with the character <code>@</code>, it expands the contents of that file into the argument list. This allows applications to handle command lines of arbitrary length.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_developer_experience">Developer Experience</h3>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/empower_developers.png" alt="empower developers" width="200"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>What are the benefits for you as developer?</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Generally a picocli application will have a lot <strong>less code</strong> than the Commons CLI equivalent. The picocli annotations allow applications to define options and positional parameters in a <strong>declarative</strong> way where all information is in one place. Also, picocli offers a number of <strong>conveniences</strong> like type conversion and automatic help that take care of some mechanics so the application can focus more on the business logic. The rest of this article will show this in more detail.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p><strong>Documentation</strong>: picocli has an extensive <a href="https://picocli.info">user manual</a> and detailed <a href="https://picocli.info/apidocs/">javadoc</a>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p><strong>Troubleshooting</strong>. Picocli has a built-in <a href="https://picocli.info/#_tracing">tracing</a> facility to facilitate troubleshooting. End users can use system property <code>picocli.trace</code> to control the trace level. Supported levels are <code>OFF</code>, <code>WARN</code>, <code>INFO</code>, and <code>DEBUG</code>. The default trace level is <code>WARN</code>.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_future_expansion">Future Expansion</h3>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/seeing-the-future.jpg" alt="seeing the future" width="200"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Finally, other than the immediate pay-off, are there any future benefits to be gained by migrating from Commons CLI to picocli?</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Picocli has a lot of <strong>advanced features</strong>. Your application may not use these features yet, but if you want to expand your application in the future, picocli has support for nested <a href="https://picocli.info/#_subcommands">subcommands</a> (and sub-subcommands to any depth), <a href="https://picocli.info/#_mixins">mixins</a> for reuse, can easily <a href="https://picocli.info/#_dependency_injection">integrate with</a> Dependency Injection containers, and a growing tool framework to <a href="https://github.com/remkop/picocli/tree/master/picocli-codegen">generate</a> source code, documentation and configuration files from a picocli <code>CommandSpec</code> model.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Finally, picocli is <strong>actively maintained</strong>, whereas Commons CLI seems to be near-dormant with 6 releases in 16 years.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_an_example_migration_checkstyle">An Example Migration: CheckStyle</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/checkstyle-logo-260x50.png" alt="checkstyle logo 260x50" width="200"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>A command line application needs to do three things:</p>
|
||
</div>
|
||
<div class="olist arabic">
|
||
<ol class="arabic">
|
||
<li>
|
||
<p>Define the supported options</p>
|
||
</li>
|
||
<li>
|
||
<p>Parse the command line arguments</p>
|
||
</li>
|
||
<li>
|
||
<p>Process the results</p>
|
||
</li>
|
||
</ol>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s compare how this is done in Commons CLI and in picocli, using CheckStyle’s <code>com.puppycrawl.tools.checkstyle.Main</code> command line utility as an example.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The full source code <a href="https://github.com/checkstyle/checkstyle/blob/c998a06ad78213e31b2449e9c9e466c2ff8222f9/src/main/java/com/puppycrawl/tools/checkstyle/Main.java">before</a> and <a href="https://github.com/checkstyle/checkstyle/blob/master/src/main/java/com/puppycrawl/tools/checkstyle/Main.java">after</a> the migration is on GitHub.</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_defining_options_and_positional_parameters">Defining Options and Positional Parameters</h3>
|
||
<div class="sect3">
|
||
<h4 id="_defining_options_with_commons_cli">Defining Options with Commons CLI</h4>
|
||
<div class="paragraph">
|
||
<p>Commons CLI has multiple ways to define options: <code>Options.addOption</code>, constructing a <code>new Options(…​)</code> and invoking methods on this object, the deprecated <code>OptionBuilder</code> class, and the recommended <code>Option.Builder</code> class.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The Checkstyle <code>Main</code> class uses the <code>Options.addOption</code> method. It starts by defining a number of constants for the option names:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="CodeRay highlight"><code data-lang="java"><span class="comment">/** Name for the option 's'. */</span>
|
||
<span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> OPTION_S_NAME = <span class="string"><span class="delimiter">"</span><span class="content">s</span><span class="delimiter">"</span></span>;
|
||
|
||
<span class="comment">/** Name for the option 't'. */</span>
|
||
<span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> OPTION_T_NAME = <span class="string"><span class="delimiter">"</span><span class="content">t</span><span class="delimiter">"</span></span>;
|
||
|
||
<span class="comment">/** Name for the option '--tree'. */</span>
|
||
<span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">String</span> OPTION_TREE_NAME = <span class="string"><span class="delimiter">"</span><span class="content">tree</span><span class="delimiter">"</span></span>;
|
||
|
||
... <span class="comment">// and more. Checkstyle Main has 26 options in total.</span></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>Main.buildOptions</code> method uses these constants to construct and return a Commons CLI <code>Options</code> object that defines the supported options:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">private</span> <span class="directive">static</span> Options buildOptions() {
|
||
<span class="directive">final</span> Options options = <span class="keyword">new</span> Options();
|
||
options.addOption(OPTION_C_NAME, <span class="predefined-constant">true</span>, <span class="string"><span class="delimiter">"</span><span class="content">Sets the check configuration file to use.</span><span class="delimiter">"</span></span>);
|
||
options.addOption(OPTION_O_NAME, <span class="predefined-constant">true</span>, <span class="string"><span class="delimiter">"</span><span class="content">Sets the output file. Defaults to stdout</span><span class="delimiter">"</span></span>);
|
||
...
|
||
options.addOption(OPTION_V_NAME, <span class="predefined-constant">false</span>, <span class="string"><span class="delimiter">"</span><span class="content">Print product version and exit</span><span class="delimiter">"</span></span>);
|
||
options.addOption(OPTION_T_NAME, OPTION_TREE_NAME, <span class="predefined-constant">false</span>,
|
||
<span class="string"><span class="delimiter">"</span><span class="content">Print Abstract Syntax Tree(AST) of the file</span><span class="delimiter">"</span></span>);
|
||
...
|
||
return options;
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_defining_options_with_picocli">Defining Options with Picocli</h4>
|
||
<div class="paragraph">
|
||
<p>In picocli you can define supported options either programmatically with builders, similar to the Commons CLI approach, or declaratively with annotations.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Picocli’s programmatic API may be useful for dynamic applications where not all options are known in advance. If you’re interested in the programmatic approach, take a look at the <code>CommandSpec</code>, <code>OptionSpec</code> and <code>PositionalParamSpec</code> classes. See also <a href="https://github.com/remkop/picocli/wiki/Programmatic-API">Programmatic API</a> for more detail.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In this article we will use the picocli annotations. For the CheckStyle example, this would look something like the below:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Option</span>(names = <span class="string"><span class="delimiter">"</span><span class="content">-c</span><span class="delimiter">"</span></span>, description = <span class="string"><span class="delimiter">"</span><span class="content">Sets the check configuration file to use.</span><span class="delimiter">"</span></span>)
|
||
<span class="directive">private</span> <span class="predefined-type">File</span> configurationFile;
|
||
|
||
<span class="annotation">@Option</span>(names = <span class="string"><span class="delimiter">"</span><span class="content">-o</span><span class="delimiter">"</span></span>, description = <span class="string"><span class="delimiter">"</span><span class="content">Sets the output file. Defaults to stdout</span><span class="delimiter">"</span></span>)
|
||
<span class="directive">private</span> <span class="predefined-type">File</span> outputFile;
|
||
|
||
<span class="annotation">@Option</span>(names = <span class="string"><span class="delimiter">"</span><span class="content">-v</span><span class="delimiter">"</span></span>, versionHelp = <span class="predefined-constant">true</span>, description = <span class="string"><span class="delimiter">"</span><span class="content">Print product version and exit</span><span class="delimiter">"</span></span>)
|
||
<span class="directive">private</span> <span class="type">boolean</span> versionHelpRequested;
|
||
|
||
<span class="annotation">@Option</span>(names = {<span class="string"><span class="delimiter">"</span><span class="content">-t</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">--tree</span><span class="delimiter">"</span></span>}, description = <span class="string"><span class="delimiter">"</span><span class="content">Print Abstract Syntax Tree(AST) of the file</span><span class="delimiter">"</span></span>)
|
||
<span class="directive">private</span> <span class="type">boolean</span> printAST;</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_comparison">Comparison</h4>
|
||
<div class="sect4">
|
||
<h5 id="_declarative">Declarative</h5>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/declare.jpg" alt="declare" width="200"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>With Commons CLI, you build a specification by calling a method with String values. One drawback of an API like this is that good style compels client code to define constants to avoid "magic values", like the Checkstyle <code>Main</code> class dutifully does.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>With picocli, all information is in one place. Annotations only accept String literals, so definition and usage are automatically placed together without the need to declare constants. This results in cleaner and less code.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_strongly_typed">Strongly Typed</h5>
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="https://picocli.info/images/Type.jpg" alt="Type" width="200">
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Commons CLI uses a boolean flag to denote whether the option takes an argument or not.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Picocli lets you use types directly. Based on the type, picocli "knows" how many arguments the option needs: <code>boolean</code> fields don’t have an argument, <code>Collection</code>, <code>Map</code> and array fields can have zero to any number of arguments, and any other type means the options takes a single argument. This can be customized (see <a href="https://picocli.info/#_arity"><code>arity</code></a>) but most of the time the default is good enough.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Picocli encourages you to use <code>enum</code> types for options or positional parameters with a limited set of valid values. Not only will picocli validate the input for you, you can also <a href="https://picocli.info/#_show_default_values">show all values</a> in the usage help message with <code>@Option(description = "Valid values: ${COMPLETION-CANDIDATES}")</code>. Enums also allow command line completion to suggest completion candidates for the values of the option.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_less_code">Less Code</h5>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/convert.png" alt="convert" width="150"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Picocli <a href="https://picocli.info/#_strongly_typed_everything">converts</a> the option parameter String value to the field type. Not only does it save the application from doing this work, it also provides some minimal validation on the user input. If the conversion fails, a <code>ParameterException</code> is thrown with a user-friendly error message.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s look at an example to see how useful this is. The Checkstyle <code>Main</code> class defines a <code>-x</code>, <code>--exclude-regexp</code> option that allows uses to specify a number of regular expressions for directories to exclude.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>With Commons CLI, you need to convert the String values that were matched on the command line to <code>java.util.regex.Pattern</code> objects in the application:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="CodeRay highlight"><code data-lang="java"><span class="comment">/**
|
||
* Gets the list of exclusions from the parse results.
|
||
* @param commandLine object representing the result of parsing the command line
|
||
* @return List of exclusion patterns.
|
||
*/</span>
|
||
<span class="directive">private</span> <span class="directive">static</span> <span class="predefined-type">List</span><<span class="predefined-type">Pattern</span>> getExclusions(CommandLine commandLine) {
|
||
<span class="directive">final</span> <span class="predefined-type">List</span><<span class="predefined-type">Pattern</span>> result = <span class="keyword">new</span> <span class="predefined-type">ArrayList</span><>();
|
||
|
||
<span class="keyword">if</span> (commandLine.hasOption(OPTION_X_NAME)) {
|
||
<span class="keyword">for</span> (<span class="predefined-type">String</span> value : commandLine.getOptionValues(OPTION_X_NAME)) {
|
||
result.add(<span class="predefined-type">Pattern</span>.compile(value));
|
||
}
|
||
}
|
||
<span class="keyword">return</span> result;
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>By contract, in picocli you would simply declare the option on a <code>List<Pattern></code> (or a <code>Pattern[]</code> array) field.
|
||
Since picocli has a built-in converter for <code>java.util.regex.Pattern</code>, all that is needed is to declare the option. The conversion code goes away completely. Picocli will instantiate and populate the list if one or more <code>-x</code> options are specified on the command line.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="CodeRay highlight"><code data-lang="java"><span class="comment">/** Option that allows users to specify a regex of paths to exclude. */</span>
|
||
<span class="annotation">@Option</span>(names = {<span class="string"><span class="delimiter">"</span><span class="content">-x</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">--exclude-regexp</span><span class="delimiter">"</span></span>},
|
||
description = <span class="string"><span class="delimiter">"</span><span class="content">Regular expression of directory to exclude from CheckStyle</span><span class="delimiter">"</span></span>)
|
||
<span class="directive">private</span> <span class="predefined-type">List</span><<span class="predefined-type">Pattern</span>> excludeRegex;</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_option_names">Option Names</h5>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/name.jpg" alt="name" width="200"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Commons CLI supports "short" and "long" options, like <code>-t</code> and <code>--tree</code>. This is not always what you want.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Picocli lets an option have any number of names, with any prefix. For example, this would be perfectly fine in picocli:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Option</span>(names = {<span class="string"><span class="delimiter">"</span><span class="content">-cp</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">-classpath</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">--class-path</span><span class="delimiter">"</span></span>})</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_positional_parameters">Positional Parameters</h5>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/location.jpg" alt="location" width="200"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In Commons CLI you cannot define positional parameters up front. Instead, its <code>CommandLine</code> parse result class has a method <code>getArgs</code> that returns the positional parameters as an array of Strings. The Checkstyle <code>Main</code> class uses this to create the list of <code>File</code> objects to process.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In picocli, <a href="https://picocli.info/#_positional_parameters">positional parameters</a> are first-class citizens, like named options. Not only can they be strongly typed, parameters at different positions can have different types, and each will have a separate entry and description listed in the usage help message.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>For example, the Checkstyle <code>Main</code> class needs a list of files to process, so we declare a field and annotate it with <code>@Parameters</code>. The <code>arity = "1..*"</code> attribute means that at least one file must be specified, or picocli will show an error message about the missing argument.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Parameters</span>(paramLabel = <span class="string"><span class="delimiter">"</span><span class="content">file</span><span class="delimiter">"</span></span>, arity = <span class="string"><span class="delimiter">"</span><span class="content">1..*</span><span class="delimiter">"</span></span>, description = <span class="string"><span class="delimiter">"</span><span class="content">The files to process</span><span class="delimiter">"</span></span>)
|
||
<span class="directive">private</span> <span class="predefined-type">List</span><<span class="predefined-type">File</span>> filesToProcess;</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_help_options">Help Options</h5>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/AskingForHelp.jpg" alt="AskingForHelp" width="200"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>It is surprisingly difficult in Commons CLI to create an application with a required option that also has a <code>--help</code> option. Commons CLI has no special treatment for help options and will complain about the missing required option when the user specifies <code><command> --help</code>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Picocli has built-in support for common (and custom) <a href="https://picocli.info/#_help_options">help options</a>.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_parsing_the_command_line_arguments">Parsing the Command Line Arguments</h3>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/pipeline.jpg" alt="pipeline" width="400"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Commons CLI has a <code>CommandLineParser</code> interface with a <code>parse</code> method that returns a <code>CommandLine</code> representing the parse result. The application then calls <code>CommandLine.hasOption(String)</code> to see if a flag was set, or <code>CommandLine.getOptionValue(String)</code> to get the option value.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Picocli populates the annotated fields as it parses the command line arguments. Picocli’s <code>parse…​</code> methods also return a <code>ParseResult</code> that can be queried on what options were specified and what value they had, but most applications don’t actually need to use the <code>ParseResult</code> class since they can simply inspect the value that were injected into the annotated fields during parsing.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_processing_the_results">Processing the Results</h3>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/processing_results.jpg" alt="processing results" width="300"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>When the parser is done, the application needs to run its business logic, but first there are some things to check:</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>Was version info or usage help requested? If so, print out the requested information and quit.</p>
|
||
</li>
|
||
<li>
|
||
<p>Was the user input invalid? Print out an error message with the details, print the usage help message and quit.</p>
|
||
</li>
|
||
<li>
|
||
<p>Finally run the business logic - and deal with errors thrown by the business logic.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>With Commons CLI, this looks something like this:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="CodeRay highlight"><code data-lang="java"><span class="type">int</span> exitStatus;
|
||
<span class="keyword">try</span> {
|
||
CommandLine commandLine = <span class="keyword">new</span> DefaultParser().parse(buildOptions(), args);
|
||
|
||
<span class="keyword">if</span> (commandLine.hasOption(OPTION_VERSION)) { <span class="comment">// --version</span>
|
||
<span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">"</span><span class="content">Checkstyle version: </span><span class="delimiter">"</span></span> + version());
|
||
exitStatus = <span class="integer">0</span>;
|
||
} <span class="keyword">else</span> <span class="keyword">if</span> (commandLine.hasOption(OPTION_HELP)) { <span class="comment">// --help</span>
|
||
printUsage(<span class="predefined-type">System</span>.out);
|
||
exitStatus = <span class="integer">0</span>;
|
||
} <span class="keyword">else</span> {
|
||
exitStatus = runBusinessLogic(); <span class="comment">// business logic</span>
|
||
}
|
||
} <span class="keyword">catch</span> (<span class="exception">ParseException</span> pex) { <span class="comment">// invalid input</span>
|
||
exitStatus = EXIT_WITH_CLI_VIOLATION;
|
||
<span class="predefined-type">System</span>.err.println(pex.getMessage());
|
||
printUsage(<span class="predefined-type">System</span>.err);
|
||
} <span class="keyword">catch</span> (CheckstyleException ex) { <span class="comment">// business logic exception</span>
|
||
exitStatus = EXIT_WITH_CHECKSTYLE_EXCEPTION_CODE;
|
||
ex.printStackTrace();
|
||
}
|
||
<span class="predefined-type">System</span>.exit(exitStatus);</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Picocli offers some convenience methods that take care of most of the above. By making your command implement <code>Runnable</code> or <code>Callable</code>, the application can focus on the business logic. At its simplest, this can look something like this:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">class</span> <span class="class">Main</span> <span class="directive">implements</span> <span class="predefined-type">Callable</span><<span class="predefined-type">Integer</span>> {
|
||
<span class="directive">public</span> <span class="directive">static</span> <span class="type">void</span> main(<span class="predefined-type">String</span><span class="type">[]</span> args) {
|
||
CommandLine.call(<span class="keyword">new</span> Main(), args);
|
||
}
|
||
|
||
<span class="directive">public</span> <span class="predefined-type">Integer</span> call() <span class="directive">throws</span> CheckstyleException {
|
||
<span class="comment">// business logic here</span>
|
||
}
|
||
}</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The Checkstyle <code>Main</code> class needs to control the exit code, and has some strict internal requirements for error handling, so we ended up not using the convenience methods and kept the parse result processing very similar to what it was with Commons CLI. Improving this area is on the picocli todo list.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_usage_help_message">Usage Help Message</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Picocli uses ANSI colors and styles in the usage help message on supported platforms.
|
||
This doesn’t just look good, it also <strong>reduces the cognitive load</strong> on the user: the contrast make the important information like commands, options, and parameters stand out from the surrounding text.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Applications can also use ANSI colors and styles in the description or other sections of the usage help message with a simple markup like <code>@|bg(red) text with red background|@</code>. See the <a href="https://picocli.info/#_usage_help_with_styles_and_colors">relevant section</a> of the user manual.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>For CheckStyle, we kept it to the bare minimum, and the resulting output for CheckStyle looks like this:</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="https://picocli.info/images/checkstyle-usage.png" alt="checkstyle usage"></span></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_wrapping_up_a_final_tip">Wrapping Up: a Final Tip</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Be aware that the Commons CLI default parser will recognize both single hyphen (<code>-</code>) and double hyphen (<code>--</code>) long options, even though the usage help message will only show options with double hyphens. You need to decide whether to continue to support this.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In picocli you can use <code>@Option(names = "-xxx", hidden = true)</code> to declare long options with a single hyphen if you want to mimic the exact same behaviour as Commons CLI: hidden options in picocli <a href="https://picocli.info/#_hidden_options_and_parameters">are not shown</a> in the usage help message.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_conclusion">Conclusion</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Migrating from Commons CLI to picocli can give end users a better user experience, and can give developers significant benefits in increased maintainability and potential for future expansion. Migration is a manual process, but is relatively straightforward.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Update: the CheckStyle project accepted a pull request with the changes in this article. From CheckStyle 8.15 its command line tools will use picocli. It looks like the CheckStyle maintainers were happy with the result:</p>
|
||
</div>
|
||
<div class="quoteblock">
|
||
<blockquote>
|
||
<div class="paragraph">
|
||
<p>Checkstyle migrated from Apache CLI to @picocli (will be released in 8.15), finally documentation of CLI arguments is now well organized in declarative way in code, and checkstyle’s CLI is following CLI best practices.</p>
|
||
</div>
|
||
</blockquote>
|
||
<div class="attribution">
|
||
— CheckStyle maintainer Roman Ivanov<br>
|
||
<cite>https://twitter.com/checkstyle_java/status/1057246772089606144</cite>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If you like what you see, please star <a href="https://github.com/remkop/picocli">picocli on GitHub</a> and tell your friends!</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="footer">
|
||
<div id="footer-text">
|
||
Version 4.5.2<br>
|
||
Last updated 2018-12-02 18:37:39 +0900
|
||
</div>
|
||
</div>
|
||
<style>
|
||
/* Stylesheet for CodeRay to match GitHub theme | MIT License | http://foundation.zurb.com */
|
||
pre.CodeRay{background:#f7f7f8}
|
||
.CodeRay .line-numbers{border-right:1px solid currentColor;opacity:.35;padding:0 .5em 0 0}
|
||
.CodeRay span.line-numbers{display:inline-block;margin-right:.75em}
|
||
.CodeRay .line-numbers strong{color:#000}
|
||
table.CodeRay{border-collapse:separate;border:0;margin-bottom:0;background:none}
|
||
table.CodeRay td{vertical-align:top;line-height:inherit}
|
||
table.CodeRay td.line-numbers{text-align:right}
|
||
table.CodeRay td.code{padding:0 0 0 .75em}
|
||
.CodeRay .debug{color:#fff !important;background:#000080 !important}
|
||
.CodeRay .annotation{color:#007}
|
||
.CodeRay .attribute-name{color:#000080}
|
||
.CodeRay .attribute-value{color:#700}
|
||
.CodeRay .binary{color:#509}
|
||
.CodeRay .comment{color:#998;font-style:italic}
|
||
.CodeRay .char{color:#04d}
|
||
.CodeRay .char .content{color:#04d}
|
||
.CodeRay .char .delimiter{color:#039}
|
||
.CodeRay .class{color:#458;font-weight:bold}
|
||
.CodeRay .complex{color:#a08}
|
||
.CodeRay .constant,.CodeRay .predefined-constant{color:#008080}
|
||
.CodeRay .color{color:#099}
|
||
.CodeRay .class-variable{color:#369}
|
||
.CodeRay .decorator{color:#b0b}
|
||
.CodeRay .definition{color:#099}
|
||
.CodeRay .delimiter{color:#000}
|
||
.CodeRay .doc{color:#970}
|
||
.CodeRay .doctype{color:#34b}
|
||
.CodeRay .doc-string{color:#d42}
|
||
.CodeRay .escape{color:#666}
|
||
.CodeRay .entity{color:#800}
|
||
.CodeRay .error{color:#808}
|
||
.CodeRay .exception{color:inherit}
|
||
.CodeRay .filename{color:#099}
|
||
.CodeRay .function{color:#900;font-weight:bold}
|
||
.CodeRay .global-variable{color:#008080}
|
||
.CodeRay .hex{color:#058}
|
||
.CodeRay .integer,.CodeRay .float{color:#099}
|
||
.CodeRay .include{color:#555}
|
||
.CodeRay .inline{color:#000}
|
||
.CodeRay .inline .inline{background:#ccc}
|
||
.CodeRay .inline .inline .inline{background:#bbb}
|
||
.CodeRay .inline .inline-delimiter{color:#d14}
|
||
.CodeRay .inline-delimiter{color:#d14}
|
||
.CodeRay .important{color:#555;font-weight:bold}
|
||
.CodeRay .interpreted{color:#b2b}
|
||
.CodeRay .instance-variable{color:#008080}
|
||
.CodeRay .label{color:#970}
|
||
.CodeRay .local-variable{color:#963}
|
||
.CodeRay .octal{color:#40e}
|
||
.CodeRay .predefined{color:#369}
|
||
.CodeRay .preprocessor{color:#579}
|
||
.CodeRay .pseudo-class{color:#555}
|
||
.CodeRay .directive{font-weight:bold}
|
||
.CodeRay .type{font-weight:bold}
|
||
.CodeRay .predefined-type{color:inherit}
|
||
.CodeRay .reserved,.CodeRay .keyword {color:#000;font-weight:bold}
|
||
.CodeRay .key{color:#808}
|
||
.CodeRay .key .delimiter{color:#606}
|
||
.CodeRay .key .char{color:#80f}
|
||
.CodeRay .value{color:#088}
|
||
.CodeRay .regexp .delimiter{color:#808}
|
||
.CodeRay .regexp .content{color:#808}
|
||
.CodeRay .regexp .modifier{color:#808}
|
||
.CodeRay .regexp .char{color:#d14}
|
||
.CodeRay .regexp .function{color:#404;font-weight:bold}
|
||
.CodeRay .string{color:#d20}
|
||
.CodeRay .string .string .string{background:#ffd0d0}
|
||
.CodeRay .string .content{color:#d14}
|
||
.CodeRay .string .char{color:#d14}
|
||
.CodeRay .string .delimiter{color:#d14}
|
||
.CodeRay .shell{color:#d14}
|
||
.CodeRay .shell .delimiter{color:#d14}
|
||
.CodeRay .symbol{color:#990073}
|
||
.CodeRay .symbol .content{color:#a60}
|
||
.CodeRay .symbol .delimiter{color:#630}
|
||
.CodeRay .tag{color:#008080}
|
||
.CodeRay .tag-special{color:#d70}
|
||
.CodeRay .variable{color:#036}
|
||
.CodeRay .insert{background:#afa}
|
||
.CodeRay .delete{background:#faa}
|
||
.CodeRay .change{color:#aaf;background:#007}
|
||
.CodeRay .head{color:#f8f;background:#505}
|
||
.CodeRay .insert .insert{color:#080}
|
||
.CodeRay .delete .delete{color:#800}
|
||
.CodeRay .change .change{color:#66f}
|
||
.CodeRay .head .head{color:#f4f}
|
||
</style>
|
||
</body>
|
||
</html> |