-
Notifications
You must be signed in to change notification settings - Fork 418
/
CustomLayoutDemo.java
261 lines (239 loc) · 13.8 KB
/
CustomLayoutDemo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/*
Copyright 2017 Remko Popma
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package picocli;
import picocli.CommandLine.Help.Ansi.Text;
import picocli.CommandLine.Help.Column;
import picocli.CommandLine.Help.IOptionRenderer;
import picocli.CommandLine.Help.IParameterRenderer;
import picocli.CommandLine.Help.Layout;
import picocli.CommandLine.Help.TextTable;
import static picocli.CommandLine.*;
import static picocli.CommandLine.Model.*;
import static picocli.CommandLine.Help.Column.Overflow.*;
/**
* Demonstrates how the CommandLine.Help API can be used to create custom layouts for usage help messages.
*/
@Command(name = "picocli.CustomLayoutDemo", description = "Demonstrates picocli custom layouts.",
footer = {
"Run with -Dpicocli.ansi=true to force picocli to use ansi codes,",
"or with -Dpicocli.ansi=false to force picocli to NOT use ansi codes.",
"By default picocli will use ansi codes if the platform supports it."
}
)
public class CustomLayoutDemo implements Runnable {
public static void main(String[] args) {
new CommandLine(new CustomLayoutDemo()).execute(args);
}
@Option(names = {"-z", "--zip"}, description = "Show usage help for a layout with 2 options per row.")
private boolean showZip;
@Option(names = {"-n", "--netstat"}, description = "Show usage help for a layout with a narrow options column and a wide description column. Descriptions that wrap to the next row are not indented.")
private boolean showNetstat;
public void run() {
if (!showZip && !showNetstat) {
CommandLine.usage(this, System.err);
return;
}
if (showZip) { System.out.println(createZipUsageFormat(Help.Ansi.AUTO)); }
if (showNetstat) { System.out.println(createNetstatUsageFormat(Help.Ansi.AUTO)); }
}
public static String createZipUsageFormat(Help.Ansi ansi) {
@Command(description = {
"Copyright (c) 1990-2008 Info-ZIP - Type 'zip \"-L\"' for software license.",
"Zip 3.0 (July 5th 2008). Command:",
"@|bold zip|@ [@|yellow -options|@] [@|yellow -b|@ @|underline path|@] [@|yellow -t|@ @|underline mmddyyyy|@] [@|yellow -n|@ @|underline suffixes|@] [@|yellow zipfile|@ @|underline list|@] [@|yellow -xi|@ @|underline list|@]",
" The default action is to add or replace zipfile entries from list, which",
" can include the special name - to compress standard input.",
" If @|yellow zipfile|@ and @|yellow list|@ are omitted, zip compresses stdin to stdout."}
)
class Zip {
@Option(names = "-f", description = "freshen: only changed files")
boolean freshen;
@Option(names = "-u", description = "update: only changed or new files")
boolean update;
@Option(names = "-d", description = "delete entries in zipfile")
boolean delete;
@Option(names = "-m", description = "move into zipfile (delete OS files)")
boolean move;
@Option(names = "-r", description = "recurse into directories")
boolean recurse;
@Option(names = "-j", description = "junk (don't record) directory names")
boolean junk;
@Option(names = "-0", description = "store only")
boolean store;
@Option(names = "-l", description = "convert LF to CR LF (@|yellow -ll|@ CR LF to LF)")
boolean lf2crlf;
@Option(names = "-1", description = "compress faster")
boolean faster;
@Option(names = "-9", description = "compress better")
boolean better;
@Option(names = "-q", description = "quiet operation")
boolean quiet;
@Option(names = "-v", description = "verbose operation/print version info")
boolean verbose;
@Option(names = "-c", description = "add one-line comments")
boolean comments;
@Option(names = "-z", description = "add zipfile comment")
boolean zipComment;
@Option(names = "-@", description = "read names from stdin")
boolean readFileList;
@Option(names = "-o", description = "make zipfile as old as latest entry")
boolean old;
@Option(names = "-x", description = "exclude the following names")
boolean exclude;
@Option(names = "-i", description = "include only the following names")
boolean include;
@Option(names = "-F", description = "fix zipfile (@|yellow -FF|@ try harder)")
boolean fix;
@Option(names = "-D", description = "do not add directory entries")
boolean directories;
@Option(names = "-A", description = "adjust self-extracting exe")
boolean adjust;
@Option(names = "-J", description = "junk zipfile prefix (unzipsfx)")
boolean junkPrefix;
@Option(names = "-T", description = "test zipfile integrity")
boolean test;
@Option(names = "-X", description = "eXclude eXtra file attributes")
boolean excludeAttribs;
@Option(names = "-y", description = "store symbolic links as the link instead of the referenced file")
boolean symbolic;
@Option(names = "-e", description = "encrypt")
boolean encrypt;
@Option(names = "-n", description = "don't compress these suffixes")
boolean dontCompress;
@Option(names = "-h2", description = "show more help")
boolean moreHelp;
}
class TwoOptionsPerRowLayout extends Layout { // define a custom layout
TextTable.Cell previous = new TextTable.Cell(0, 0);
private TwoOptionsPerRowLayout(Help.ColorScheme colorScheme, TextTable textTable,
IOptionRenderer optionRenderer,
IParameterRenderer parameterRenderer) {
super(colorScheme, textTable, optionRenderer, parameterRenderer);
}
@Override
public void layout(ArgSpec arg, Text[][] values) {
Text[] columnValues = values[0]; // we know renderer creates a single row with two values
// We want to show two options on one row, next to each other,
// unless the first option spanned multiple columns (in which case there are not enough columns left)
int col = previous.column 1;
if (col == 1 || col columnValues.length > table.columns().length) { // if true, write into next row
// table also adds an empty row if a text value spanned multiple columns
if (table.rowCount() == 0 || table.rowCount() == previous.row 1) { // avoid adding 2 empty rows
table.addEmptyRow(); // create the slots to write the text values into
}
col = 0; // we are starting a new row, reset the column to write into
}
for (int i = 0; i < columnValues.length; i ) {
// always write to the last row, column depends on what happened previously
previous = table.putValue(table.rowCount() - 1, col i, columnValues[i]);
}
}
}
@SuppressWarnings("deprecation")
TextTable textTable = TextTable.forColumns(ansi,
new Column(5, 2, TRUNCATE), // values should fit
new Column(30, 2, SPAN), // overflow into adjacent columns
new Column(4, 1, TRUNCATE), // values should fit again
new Column(39, 2, WRAP));
TwoOptionsPerRowLayout layout = new TwoOptionsPerRowLayout(
Help.defaultColorScheme(ansi),
textTable,
Help.createMinimalOptionRenderer(),
Help.createMinimalParameterRenderer());
Help help = new Help(new Zip(), ansi);
StringBuilder sb = new StringBuilder();
sb.append(help.description()); // show the first 6 lines, including copyright, description and usage
// Note that we don't sort the options, so they appear in the order the fields are declared in the Zip class.
layout.addOptions(help.options(), help.parameterLabelRenderer());
sb.append(layout); // finally, copy the options details help text into the StringBuilder
return sb.toString();
}
/** for Netstat test */
private enum Protocol {IP, IPv6, ICMP, ICMPv6, TCP, TCPv6, UDP, UDPv6}
public static String createNetstatUsageFormat(Help.Ansi ansi) {
@Command(name = "NETSTAT",
separator = " ",
abbreviateSynopsis = true,
header = "Displays protocol statistics and current TCP/IP network connections.%n")
class Netstat {
@Option(names="-a", description="Displays all connections and listening ports.")
boolean displayAll;
@Option(names="-b", description="Displays the executable involved in creating each connection or "
"listening port. In some cases well-known executables host "
"multiple independent components, and in these cases the "
"sequence of components involved in creating the connection "
"or listening port is displayed. In this case the executable "
"name is in [] at the bottom, on top is the component it called, "
"and so forth until TCP/IP was reached. Note that this option "
"can be time-consuming and will fail unless you have sufficient "
"permissions.")
boolean displayExecutable;
@Option(names="-e", description="Displays Ethernet statistics. This may be combined with the -s option.")
boolean displayEthernetStats;
@Option(names="-f", description="Displays Fully Qualified Domain Names (FQDN) for foreign addresses.")
boolean displayFQCN;
@Option(names="-n", description="Displays addresses and port numbers in numerical form.")
boolean displayNumerical;
@Option(names="-o", description="Displays the owning process ID associated with each connection.")
boolean displayOwningProcess;
@Option(names="-p", paramLabel = "proto",
description="Shows connections for the protocol specified by proto; proto "
"may be any of: TCP, UDP, TCPv6, or UDPv6. If used with the -s "
"option to display per-protocol statistics, proto may be any of: "
"IP, IPv6, ICMP, ICMPv6, TCP, TCPv6, UDP, or UDPv6.")
Protocol proto;
@Option(names="-q", description="Displays all connections, listening ports, and bound "
"nonlistening TCP ports. Bound nonlistening ports may or may not "
"be associated with an active connection.")
boolean query;
@Option(names="-r", description="Displays the routing table.")
boolean displayRoutingTable;
@Option(names="-s", description="Displays per-protocol statistics. By default, statistics are "
"shown for IP, IPv6, ICMP, ICMPv6, TCP, TCPv6, UDP, and UDPv6; "
"the -p option may be used to specify a subset of the default.")
boolean displayStatistics;
@Option(names="-t", description="Displays the current connection offload state.")
boolean displayOffloadState;
@Option(names="-x", description="Displays NetworkDirect connections, listeners, and shared endpoints.")
boolean displayNetDirect;
@Option(names="-y", description="Displays the TCP connection template for all connections. "
"Cannot be combined with the other options.")
boolean displayTcpConnectionTemplate;
@Parameters(arity = "0..1", paramLabel = "interval", description = ""
"Redisplays selected statistics, pausing interval seconds "
"between each display. Press CTRL C to stop redisplaying "
"statistics. If omitted, netstat will print the current "
"configuration information once.")
int interval;
}
StringBuilder sb = new StringBuilder();
Help help = new Help(new Netstat(), ansi);
help.synopsisHeading("");
sb.append(help.header()).append(help.detailedSynopsis(0, null, false));
sb.append(System.getProperty("line.separator"));
@SuppressWarnings("deprecation")
TextTable textTable = TextTable.forColumns(ansi,
new Column(15, 2, TRUNCATE),
new Column(65, 1, WRAP));
textTable.indentWrappedLines = 0;
Layout layout = new Layout(
Help.defaultColorScheme(ansi),
textTable,
Help.createMinimalOptionRenderer(),
Help.createMinimalParameterRenderer());
layout.addOptions(help.options(), help.parameterLabelRenderer());
layout.addPositionalParameters(help.positionalParameters(), Help.createMinimalParamLabelRenderer());
sb.append(layout);
return sb.toString();
}
}