1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.cli2.option;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.Comparator;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.ListIterator;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.SortedMap;
30 import java.util.TreeMap;
31
32 import org.apache.commons.cli2.Argument;
33 import org.apache.commons.cli2.DisplaySetting;
34 import org.apache.commons.cli2.Group;
35 import org.apache.commons.cli2.HelpLine;
36 import org.apache.commons.cli2.Option;
37 import org.apache.commons.cli2.OptionException;
38 import org.apache.commons.cli2.WriteableCommandLine;
39 import org.apache.commons.cli2.resource.ResourceConstants;
40
41
42
43
44 public class GroupImpl
45 extends OptionImpl implements Group {
46 private final String name;
47 private final String description;
48 private final List options;
49 private final int minimum;
50 private final int maximum;
51 private final List anonymous;
52 private final SortedMap optionMap;
53 private final Set prefixes;
54
55
56
57
58
59
60
61
62
63
64
65 public GroupImpl(final List options,
66 final String name,
67 final String description,
68 final int minimum,
69 final int maximum,
70 final boolean required) {
71 super(0, required);
72
73 this.name = name;
74 this.description = description;
75 this.minimum = minimum;
76 this.maximum = maximum;
77
78
79
80 this.options = Collections.unmodifiableList(options);
81
82
83 final List newAnonymous = new ArrayList();
84
85
86 final SortedMap newOptionMap = new TreeMap(ReverseStringComparator.getInstance());
87
88
89 final Set newPrefixes = new HashSet();
90
91
92 for (final Iterator i = options.iterator(); i.hasNext();) {
93 final Option option = (Option) i.next();
94 option.setParent(this);
95
96 if (option instanceof Argument) {
97 i.remove();
98 newAnonymous.add(option);
99 } else {
100 final Set triggers = option.getTriggers();
101
102 for (Iterator j = triggers.iterator(); j.hasNext();) {
103 newOptionMap.put(j.next(), option);
104 }
105
106
107 newPrefixes.addAll(option.getPrefixes());
108 }
109 }
110
111 this.anonymous = Collections.unmodifiableList(newAnonymous);
112 this.optionMap = Collections.unmodifiableSortedMap(newOptionMap);
113 this.prefixes = Collections.unmodifiableSet(newPrefixes);
114 }
115
116 public boolean canProcess(final WriteableCommandLine commandLine,
117 final String arg) {
118 if (arg == null) {
119 return false;
120 }
121
122
123 if (optionMap.containsKey(arg)) {
124 return true;
125 }
126
127
128 final Map tailMap = optionMap.tailMap(arg);
129
130
131 for (final Iterator iter = tailMap.values().iterator(); iter.hasNext();) {
132 final Option option = (Option) iter.next();
133
134 if (option.canProcess(commandLine, arg)) {
135 return true;
136 }
137 }
138
139 if (looksLikeOption(commandLine, arg)) {
140 return false;
141 }
142
143
144 if (anonymous.size() > 0) {
145 return true;
146 }
147
148 return false;
149 }
150
151 public Set getPrefixes() {
152 return prefixes;
153 }
154
155 public Set getTriggers() {
156 return optionMap.keySet();
157 }
158
159 public void process(final WriteableCommandLine commandLine,
160 final ListIterator arguments)
161 throws OptionException {
162 String previous = null;
163
164
165 while (arguments.hasNext()) {
166
167 final String arg = (String) arguments.next();
168
169
170 if (arg == previous) {
171
172 arguments.previous();
173
174 break;
175 }
176
177
178 previous = arg;
179
180 final Option opt = (Option) optionMap.get(arg);
181
182
183 if (opt != null) {
184 arguments.previous();
185 opt.process(commandLine, arguments);
186 }
187
188 else {
189
190
191 if (looksLikeOption(commandLine, arg)) {
192
193 final Collection values = optionMap.tailMap(arg).values();
194
195 boolean foundMemberOption = false;
196
197 for (Iterator i = values.iterator(); i.hasNext() && !foundMemberOption;) {
198 final Option option = (Option) i.next();
199
200 if (option.canProcess(commandLine, arg)) {
201 foundMemberOption = true;
202 arguments.previous();
203 option.process(commandLine, arguments);
204 }
205 }
206
207
208 if (!foundMemberOption) {
209 arguments.previous();
210
211 return;
212 }
213 }
214
215
216 else {
217
218 arguments.previous();
219
220
221
222 if (anonymous.isEmpty()) {
223 break;
224 }
225
226
227
228 for (final Iterator i = anonymous.iterator(); i.hasNext();) {
229 final Argument argument = (Argument) i.next();
230
231 if (argument.canProcess(commandLine, arguments)) {
232 argument.process(commandLine, arguments);
233 }
234 }
235 }
236 }
237 }
238 }
239
240 public void validate(final WriteableCommandLine commandLine)
241 throws OptionException {
242
243 int present = 0;
244
245
246 Option unexpected = null;
247
248 for (final Iterator i = options.iterator(); i.hasNext();) {
249 final Option option = (Option) i.next();
250
251
252 boolean validate = option.isRequired();
253
254
255 if (commandLine.hasOption(option)) {
256 if (++present > maximum) {
257 unexpected = option;
258
259 break;
260 }
261 validate = true;
262 }
263
264 if (validate) {
265 option.validate(commandLine);
266 }
267 }
268
269
270 if (unexpected != null) {
271 throw new OptionException(this, ResourceConstants.UNEXPECTED_TOKEN,
272 unexpected.getPreferredName());
273 }
274
275
276 if (present < minimum) {
277 throw new OptionException(this, ResourceConstants.MISSING_OPTION);
278 }
279
280
281 for (final Iterator i = anonymous.iterator(); i.hasNext();) {
282 final Option option = (Option) i.next();
283 option.validate(commandLine);
284 }
285 }
286
287 public String getPreferredName() {
288 return name;
289 }
290
291 public String getDescription() {
292 return description;
293 }
294
295 public void appendUsage(final StringBuffer buffer,
296 final Set helpSettings,
297 final Comparator comp) {
298 appendUsage(buffer, helpSettings, comp, "|");
299 }
300
301 public void appendUsage(final StringBuffer buffer,
302 final Set helpSettings,
303 final Comparator comp,
304 final String separator) {
305 final Set helpSettingsCopy = new HashSet(helpSettings);
306
307 final boolean optional = !isRequired()
308 && (helpSettingsCopy.contains(DisplaySetting.DISPLAY_OPTIONAL) ||
309 helpSettingsCopy.contains(DisplaySetting.DISPLAY_OPTIONAL_CHILD_GROUP));
310
311 final boolean expanded =
312 (name == null) || helpSettingsCopy.contains(DisplaySetting.DISPLAY_GROUP_EXPANDED);
313
314 final boolean named =
315 !expanded ||
316 ((name != null) && helpSettingsCopy.contains(DisplaySetting.DISPLAY_GROUP_NAME));
317
318 final boolean arguments = helpSettingsCopy.contains(DisplaySetting.DISPLAY_GROUP_ARGUMENT);
319
320 final boolean outer = helpSettingsCopy.contains(DisplaySetting.DISPLAY_GROUP_OUTER);
321
322 helpSettingsCopy.remove(DisplaySetting.DISPLAY_GROUP_OUTER);
323
324 final boolean both = named && expanded;
325
326 if (optional) {
327 buffer.append('[');
328 }
329
330 if (named) {
331 buffer.append(name);
332 }
333
334 if (both) {
335 buffer.append(" (");
336 }
337
338 if (expanded) {
339 final Set childSettings;
340
341 if (!helpSettingsCopy.contains(DisplaySetting.DISPLAY_GROUP_EXPANDED)) {
342 childSettings = DisplaySetting.NONE;
343 } else {
344 childSettings = new HashSet(helpSettingsCopy);
345 childSettings.remove(DisplaySetting.DISPLAY_OPTIONAL);
346 }
347
348
349 final List list;
350
351 if (comp == null) {
352
353 list = options;
354 } else {
355
356 list = new ArrayList(options);
357 Collections.sort(list, comp);
358 }
359
360
361 for (final Iterator i = list.iterator(); i.hasNext();) {
362 final Option option = (Option) i.next();
363
364
365 option.appendUsage(buffer, childSettings, comp);
366
367
368 if (i.hasNext()) {
369 buffer.append(separator);
370 }
371 }
372 }
373
374 if (both) {
375 buffer.append(')');
376 }
377
378 if (optional && outer) {
379 buffer.append(']');
380 }
381
382 if (arguments) {
383 for (final Iterator i = anonymous.iterator(); i.hasNext();) {
384 buffer.append(' ');
385
386 final Option option = (Option) i.next();
387 option.appendUsage(buffer, helpSettingsCopy, comp);
388 }
389 }
390
391 if (optional && !outer) {
392 buffer.append(']');
393 }
394 }
395
396 public List helpLines(final int depth,
397 final Set helpSettings,
398 final Comparator comp) {
399 final List helpLines = new ArrayList();
400
401 if (helpSettings.contains(DisplaySetting.DISPLAY_GROUP_NAME)) {
402 final HelpLine helpLine = new HelpLineImpl(this, depth);
403 helpLines.add(helpLine);
404 }
405
406 if (helpSettings.contains(DisplaySetting.DISPLAY_GROUP_EXPANDED)) {
407
408 final List list;
409
410 if (comp == null) {
411
412 list = options;
413 } else {
414
415 list = new ArrayList(options);
416 Collections.sort(list, comp);
417 }
418
419
420 for (final Iterator i = list.iterator(); i.hasNext();) {
421 final Option option = (Option) i.next();
422 helpLines.addAll(option.helpLines(depth + 1, helpSettings, comp));
423 }
424 }
425
426 if (helpSettings.contains(DisplaySetting.DISPLAY_GROUP_ARGUMENT)) {
427 for (final Iterator i = anonymous.iterator(); i.hasNext();) {
428 final Option option = (Option) i.next();
429 helpLines.addAll(option.helpLines(depth + 1, helpSettings, comp));
430 }
431 }
432
433 return helpLines;
434 }
435
436
437
438
439
440
441 public List getOptions() {
442 return options;
443 }
444
445
446
447
448
449 public List getAnonymous() {
450 return anonymous;
451 }
452
453 public Option findOption(final String trigger) {
454 final Iterator i = getOptions().iterator();
455
456 while (i.hasNext()) {
457 final Option option = (Option) i.next();
458 final Option found = option.findOption(trigger);
459
460 if (found != null) {
461 return found;
462 }
463 }
464
465 return null;
466 }
467
468 public int getMinimum() {
469 return minimum;
470 }
471
472 public int getMaximum() {
473 return maximum;
474 }
475
476
477
478
479
480
481
482
483 public boolean isRequired()
484 {
485 return (getParent() == null || super.isRequired()) && getMinimum() > 0;
486 }
487
488 public void defaults(final WriteableCommandLine commandLine) {
489 super.defaults(commandLine);
490
491 for (final Iterator i = options.iterator(); i.hasNext();) {
492 final Option option = (Option) i.next();
493 option.defaults(commandLine);
494 }
495
496 for (final Iterator i = anonymous.iterator(); i.hasNext();) {
497 final Option option = (Option) i.next();
498 option.defaults(commandLine);
499 }
500 }
501
502
503
504
505
506
507
508
509
510
511 private boolean looksLikeOption(final WriteableCommandLine commandLine,
512 final String trigger) {
513 Option oldOption = commandLine.getCurrentOption();
514 try {
515 commandLine.setCurrentOption(this);
516 return commandLine.looksLikeOption(trigger);
517 } finally {
518 commandLine.setCurrentOption(oldOption);
519 }
520 }
521 }
522
523
524 class ReverseStringComparator implements Comparator {
525 private static final Comparator instance = new ReverseStringComparator();
526
527 private ReverseStringComparator() {
528
529 }
530
531
532
533
534
535 public static final Comparator getInstance() {
536 return instance;
537 }
538
539 public int compare(final Object o1,
540 final Object o2) {
541 final String s1 = (String) o1;
542 final String s2 = (String) o2;
543
544 return -s1.compareTo(s2);
545 }
546 }