1 package com.lexicalscope.jewel.cli;
2
3 import static java.util.Arrays.asList;
4
5 import java.util.List;
6
7 import com.lexicalscope.fluentreflection.FluentMethod;
8 import com.lexicalscope.jewel.cli.specification.OptionSpecification;
9 import com.lexicalscope.jewel.cli.specification.SpecificationMultiplicity;
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 abstract class AbstractOptionSpecification implements OptionSpecification, Comparable<OptionSpecification> {
28 protected final OptionAdapter annotation;
29 private final List<String> defaultValue;
30
31 public AbstractOptionSpecification(final OptionAdapter annotation) {
32 this.annotation = annotation;
33
34 if (annotation.defaultToNull() && annotation.hasDefaultValue())
35 {
36 throw new InvalidOptionSpecificationException("option cannot have null default and non-null default value: "
37 + annotation.method());
38 }
39 else if (annotation.defaultToNull())
40 {
41 if (annotation.isMultiValued())
42 {
43 defaultValue = null;
44 }
45 else
46 {
47 defaultValue = asList((String) null);
48 }
49 }
50 else if (annotation.hasDefaultValue())
51 {
52 defaultValue = asList(annotation.defaultValue());
53 }
54 else
55 {
56 defaultValue = null;
57 }
58
59 if(hasExactCount() && exactly() < minimum() || exactly() > maximum())
60 {
61 throw new InvalidOptionSpecificationException("option has maximum and minimum and exact count which can never be satisfied: "
62 + annotation.method());
63 }
64 else if(minimum() > maximum())
65 {
66 throw new InvalidOptionSpecificationException("minimum cannot be greater than maximum: "
67 + annotation.method());
68 }
69 else if(maximum() < 0)
70 {
71 throw new InvalidOptionSpecificationException("maximum must not be less than zero: "
72 + annotation.method());
73 }
74 }
75
76 @Override public final List<String> getDefaultValue() {
77 return defaultValue;
78 }
79
80 @Override public final boolean hasDefaultValue() {
81 return getDefaultValue() != null || annotation.defaultToNull();
82 }
83
84 @Override public final String getDescription() {
85 return annotation.description();
86 }
87
88 @Override public final Class<?> getType() {
89 return annotation.getValueType().classUnderReflection();
90 }
91
92 @Override public final boolean isMultiValued() {
93 return annotation.isMultiValued();
94 }
95
96 @Override public final boolean isOptional() {
97 return getOptionalityMethod() != null || isBoolean() || hasDefaultValue();
98 }
99
100 public abstract boolean isBoolean();
101
102 @Override public final String getCanonicalIdentifier() {
103 return getMethod().property();
104 }
105
106 @Override public final FluentMethod getMethod() {
107 return annotation.method();
108 }
109
110 @Override public final FluentMethod getOptionalityMethod() {
111 return annotation.correspondingOptionalityMethod();
112 }
113
114 @Override public final int compareTo(final OptionSpecification other) {
115 return getCanonicalIdentifier().compareTo(other.getCanonicalIdentifier());
116 }
117
118 @Override public final boolean isHidden() {
119 return annotation.isHidden();
120 }
121
122 @Override public boolean allowedThisManyValues(final int count) {
123 if(count == 0 && !hasValue())
124 {
125 return true;
126 }
127 else if(count == 1 && hasValue() && !isMultiValued())
128 {
129 return true;
130 }
131 else if(isMultiValued() && hasExactCount())
132 {
133 return count == exactly();
134 }
135 else if(isMultiValued())
136 {
137 return minimum() <= count && count <= maximum();
138 }
139 return false;
140 }
141
142 @Override
143 public final int maximum() {
144 return annotation.maximum();
145 }
146
147 @Override
148 public final int minimum() {
149 return annotation.minimum();
150 }
151
152 @Override
153 public final int exactly() {
154 return annotation.exactly();
155 }
156
157 @Override
158 public final boolean hasExactCount() {
159 return annotation.exactly() >= 0;
160 }
161
162 @Override public <T> T compareCountToSpecification(
163 final int valueCount,
164 final SpecificationMultiplicity<T> specificationMultiplicity) {
165 if(!hasValue() && valueCount > 0) {
166 return specificationMultiplicity.expectedNoneGotSome();
167 } else if(!isMultiValued() && hasValue() && valueCount == 0) {
168 return specificationMultiplicity.expectedOneGotNone();
169 } else if(!isMultiValued() && valueCount > 1) {
170 return specificationMultiplicity.expectedOneGotSome();
171 } else if(isMultiValued()) {
172 if(hasExactCount() && valueCount != exactly()) {
173 if(valueCount < exactly()) {
174 return specificationMultiplicity.expectedExactGotTooFew(exactly(), valueCount);
175 } else {
176 return specificationMultiplicity.expectedExactGotTooMany(exactly(), valueCount);
177 }
178 } else if(valueCount < minimum()) {
179 return specificationMultiplicity.expectedMinimumGotTooFew(minimum(), valueCount);
180 } else if(valueCount > maximum()) {
181 return specificationMultiplicity.expectedMaximumGotTooMany(maximum(), valueCount);
182 }
183 }
184 return specificationMultiplicity.allowed();
185 }
186
187 @Override public int maximumArgumentConsumption() {
188 if(isMultiValued()) {
189 if(hasExactCount()) {
190 return exactly();
191 } else {
192 return maximum();
193 }
194 } else if (hasValue()) {
195 return 1;
196 }
197 return 0;
198 }
199 }