1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package com.lexicalscope.jewel.cli;
16
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.Iterator;
20 import java.util.LinkedHashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.TreeMap;
25 import java.util.TreeSet;
26
27 import com.lexicalscope.fluent.FluentDollar;
28 import com.lexicalscope.fluent.list.FluentList;
29 import com.lexicalscope.fluentreflection.FluentClass;
30 import com.lexicalscope.fluentreflection.FluentMethod;
31 import com.lexicalscope.jewel.cli.specification.CliSpecification;
32 import com.lexicalscope.jewel.cli.specification.OptionsSpecification;
33 import com.lexicalscope.jewel.cli.specification.ParsedOptionSpecification;
34 import com.lexicalscope.jewel.cli.specification.UnparsedOptionSpecification;
35
36 class OptionsSpecificationImpl<O> implements OptionsSpecification<O>, CliSpecification {
37 private final FluentClass<O> klass;
38
39 private final Set<ParsedOptionSpecification> options;
40 private final Map<String, ParsedOptionSpecification> optionsByName =
41 new TreeMap<String, ParsedOptionSpecification>();
42 private final Map<FluentMethod, ParsedOptionSpecification> optionsMethod =
43 new HashMap<FluentMethod, ParsedOptionSpecification>();
44 private final Map<FluentMethod, ParsedOptionSpecification> optionalOptionsMethod =
45 new HashMap<FluentMethod, ParsedOptionSpecification>();
46
47 private final Map<FluentMethod, UnparsedOptionSpecification> unparsedOptionsMethod =
48 new HashMap<FluentMethod, UnparsedOptionSpecification>();
49 private final Map<FluentMethod, UnparsedOptionSpecification> unparsedOptionalOptionsMethod =
50 new HashMap<FluentMethod, UnparsedOptionSpecification>();
51
52 OptionsSpecificationImpl(
53 final FluentClass<O> klass,
54 final List<ParsedOptionSpecification> optionSpecifications,
55 final List<UnparsedOptionSpecification> unparsedSpecifications) {
56 this.klass = klass;
57
58 if(klass.annotatedWith(CommandLineInterface.class) &&
59 klass.annotation(CommandLineInterface.class).order().equals(OptionOrder.DEFINITION))
60 {
61 options = new LinkedHashSet<ParsedOptionSpecification>();
62 }
63 else
64 {
65 options = new TreeSet<ParsedOptionSpecification>();
66 }
67
68 for (final ParsedOptionSpecification optionSpecification : optionSpecifications) {
69 addOption(optionSpecification);
70 }
71
72 for (final UnparsedOptionSpecification optionSpecification : unparsedSpecifications) {
73 addUnparsedOption(optionSpecification);
74 }
75 }
76
77 @Override public boolean isSpecified(final String key) {
78 return optionsByName.containsKey(key);
79 }
80
81 @Override public ParsedOptionSpecification getSpecification(final String key) {
82 return optionsByName.get(key);
83 }
84
85 @Override public ParsedOptionSpecification getSpecification(final FluentMethod method) {
86 if (optionsMethod.containsKey(method)) {
87 return optionsMethod.get(method);
88 }
89 return optionalOptionsMethod.get(method);
90 }
91
92 @Override public FluentList<ParsedOptionSpecification> getMandatoryOptions() {
93 final FluentList<ParsedOptionSpecification> result = FluentDollar.$.list(ParsedOptionSpecification.class);
94 for (final ParsedOptionSpecification specification : options) {
95 if (!specification.isOptional() && !specification.hasDefaultValue()) {
96 result.add(specification);
97 }
98 }
99
100 return result;
101 }
102
103 @Override public Iterator<ParsedOptionSpecification> iterator() {
104 return new ArrayList<ParsedOptionSpecification>(optionsByName.values()).iterator();
105 }
106
107 @Override public UnparsedOptionSpecification getUnparsedSpecification() {
108 return unparsedOptionsMethod.values().iterator().next();
109 }
110
111 @Override public boolean hasUnparsedSpecification() {
112 return !unparsedOptionsMethod.values().isEmpty();
113 }
114
115 @Override public String getApplicationName() {
116 final String applicationName = applicationName();
117 if (applicationName != null)
118 {
119 return applicationName;
120 }
121 return klass.name();
122 }
123
124 private String applicationName() {
125 if (klass.annotatedWith(CommandLineInterface.class))
126 {
127 final String applicationName = klass.annotation(CommandLineInterface.class).application();
128 if (applicationName != null && !applicationName.trim().equals("")) {
129 return applicationName.trim();
130 }
131 }
132 return null;
133 }
134
135 private void addOption(final ParsedOptionSpecification optionSpecification) {
136 for (final String name : optionSpecification.getNames()) {
137 optionsByName.put(name, optionSpecification);
138 }
139 options.add(optionSpecification);
140
141 optionsMethod.put(optionSpecification.getMethod(), optionSpecification);
142
143 if (optionSpecification.isOptional()) {
144 optionalOptionsMethod.put(optionSpecification.getOptionalityMethod(), optionSpecification);
145 }
146 }
147
148 private void addUnparsedOption(final UnparsedOptionSpecification optionSpecification) {
149 unparsedOptionsMethod.put(optionSpecification.getMethod(), optionSpecification);
150
151 if (optionSpecification.isOptional()) {
152 unparsedOptionalOptionsMethod.put(optionSpecification.getOptionalityMethod(), optionSpecification);
153 }
154 }
155
156 @Override public void describeTo(final HelpMessage helpMessage) {
157 if (!hasCustomApplicationName() && (!hasUnparsedSpecification() || getUnparsedSpecification().isHidden())) {
158 helpMessage.noUsageInformation();
159 } else {
160 if (hasCustomApplicationName()) {
161 helpMessage.hasUsageInformation(applicationName());
162 }
163 else
164 {
165 helpMessage.hasUsageInformation();
166 }
167
168 if (getMandatoryOptions().isEmpty()) {
169 helpMessage.hasOnlyOptionalOptions();
170 }
171 else
172 {
173 helpMessage.hasSomeMandatoryOptions();
174 }
175
176 if (hasUnparsedSpecification() && !getUnparsedSpecification().isHidden()) {
177 if (getUnparsedSpecification().isMultiValued()) {
178 helpMessage.hasUnparsedMultiValuedOption(getUnparsedSpecification().getValueName());
179 }
180 else
181 {
182 helpMessage.hasUnparsedOption(getUnparsedSpecification().getValueName());
183 }
184 }
185 }
186
187 helpMessage.startOfOptions();
188
189 for (final ParsedOptionSpecification specification : options) {
190 if(!specification.isHidden())
191 {
192 new ParsedOptionSummary(specification).describeOptionTo(helpMessage.option());
193 }
194 }
195
196 helpMessage.endOfOptions();
197 }
198
199 private boolean hasCustomApplicationName() {
200 return !nullOrBlank(applicationName());
201 }
202
203 static boolean nullOrBlank(final String string) {
204 return string == null || string.trim().equals("");
205 }
206
207 @Override public String toString() {
208 final HelpMessageBuilderImpl helpMessage = new HelpMessageBuilderImpl();
209 describeTo(helpMessage);
210 return helpMessage.toString();
211 }
212 }