Skip to content

Commit f376793

Browse files
authored
Adding the enum constants to the type displayed for an EnumSet Option in the usage string. (#24)
1 parent a6385fe commit f376793

File tree

3 files changed

+104
-10
lines changed

3 files changed

+104
-10
lines changed

olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/DescribeConfigurable.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public static TreeMap<String,FieldInfo> generateFieldInfo(Class<? extends Config
203203
Object[] constants = listType.getEnumConstants();
204204
List<String> enumConstants = new ArrayList<>();
205205
for (Object o : constants) {
206-
enumConstants.add(((Enum)o).name());
206+
enumConstants.add(((Enum<?>)o).name());
207207
}
208208
fi = new FieldInfo(f.getName(),f.getType().getName(),f,configAnnotation,defaultVal,listType.getCanonicalName(),enumConstants);
209209
} else {

olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Options.java

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,15 @@
3434
import java.lang.reflect.Field;
3535
import java.lang.reflect.InvocationTargetException;
3636
import java.lang.reflect.Modifier;
37+
import java.lang.reflect.ParameterizedType;
38+
import java.lang.reflect.Type;
3739
import java.security.AccessController;
3840
import java.security.PrivilegedAction;
3941
import java.util.ArrayDeque;
4042
import java.util.ArrayList;
4143
import java.util.Arrays;
4244
import java.util.Collections;
45+
import java.util.EnumSet;
4346
import java.util.HashMap;
4447
import java.util.HashSet;
4548
import java.util.LinkedHashMap;
@@ -77,6 +80,11 @@ default public String getOptionsDescription() {
7780
return "";
7881
}
7982

83+
/**
84+
* Constructs a formatted usage string from a table of fields.
85+
* @param usageList The fields.
86+
* @return A usage string.
87+
*/
8088
public static String formatUsage(List<List<String>> usageList) {
8189
int[] maxWidth = new int[5];
8290

@@ -118,6 +126,11 @@ public static String formatUsage(List<List<String>> usageList) {
118126
return builder.toString();
119127
}
120128

129+
/**
130+
* Constructs the usage string from the supplied Options subclass.
131+
* @param options The options to construct a usage for.
132+
* @return The usage string.
133+
*/
121134
public static List<List<String>> getUsage(Class<? extends Options> options) {
122135
ArrayList<List<String>> list = new ArrayList<>();
123136
ArrayList<List<String>> optionsList = new ArrayList<>();
@@ -175,28 +188,70 @@ public static List<List<String>> getUsage(Class<? extends Options> options) {
175188
}
176189
}
177190

191+
/**
192+
* Returns a String representing the Enum constants from this class surrounded by '{', '}'
193+
* and separated by a comma and a space.
194+
* @param enumClazz The enum class to represent.
195+
* @return A String containing all the enum constants.
196+
*/
197+
public static String getEnumConstantString(Class<? extends Enum<?>> enumClazz) {
198+
Enum<?>[] constants = enumClazz.getEnumConstants();
199+
StringBuilder sb = new StringBuilder();
200+
sb.append('{');
201+
for (Enum<?> o : constants) {
202+
sb.append(o.name());
203+
sb.append(", ");
204+
}
205+
sb.replace(sb.length() - 2, sb.length(), "}");
206+
return sb.toString();
207+
}
208+
178209
public static String generateTypeDescription(Field f) {
179210
Class<?> clazz = f.getType();
180211
if (clazz.isEnum()) {
181-
Object[] constants = clazz.getEnumConstants();
182-
StringBuilder sb = new StringBuilder();
183-
sb.append("enum - {");
184-
for (Object o : constants) {
185-
sb.append(((Enum)o).name());
186-
sb.append(", ");
212+
@SuppressWarnings("unchecked") //guarded by isEnum check
213+
Class<? extends Enum<?>> enumClazz = (Class<? extends Enum<?>>) clazz;
214+
return "enum - " + getEnumConstantString(enumClazz);
215+
} else if (clazz == EnumSet.class) {
216+
Type type = f.getGenericType();
217+
if (type instanceof ParameterizedType) {
218+
ParameterizedType typeName = (ParameterizedType) type;
219+
// Should only have a single type parameter
220+
Type enumType = typeName.getActualTypeArguments()[0];
221+
try {
222+
@SuppressWarnings("unchecked") // type parameter to an enumset must be an enum
223+
Class<? extends Enum<?>> enumClazz = (Class<? extends Enum<?>>) Class.forName(enumType.getTypeName());
224+
return "EnumSet - " + getEnumConstantString(enumClazz);
225+
} catch (ClassNotFoundException e) {
226+
Logger.getLogger(Options.class.getName()).warning("Failed to load enum class '" + enumType.getTypeName() + "'");
227+
return typeName.getTypeName();
228+
}
229+
} else {
230+
return f.getGenericType().getTypeName();
187231
}
188-
sb.replace(sb.length()-2,sb.length(),"}");
189-
return sb.toString();
190232
} else {
191233
return f.getGenericType().getTypeName();
192234
}
193235
}
194236

237+
/**
238+
* Gets the fields for this option's usage string.
239+
* @param option The option annotation.
240+
* @param f The annotated field.
241+
* @param obj The parent options object, used to access the default value for this field.
242+
* @return The fields for the usage string.
243+
*/
195244
public static ArrayList<String> getOptionUsage(Option option, Field f, Options obj) {
196245
String typeString = generateTypeDescription(f);
197246
return getOptionUsage(option,f,obj,typeString);
198247
}
199248

249+
/**
250+
* Gets the usage for one of the default options (which don't have a parent options object).
251+
* @param option The option annotation.
252+
* @param type The type of the option.
253+
* @return The fields for the usage string.
254+
*/
200255
public static ArrayList<String> getOptionUsage(Option option, String type) {
201256
ArrayList<String> output = new ArrayList<>();
202257
if (option.charName() != Option.EMPTY_CHAR) {
@@ -211,6 +266,14 @@ public static ArrayList<String> getOptionUsage(Option option, String type) {
211266
return output;
212267
}
213268

269+
/**
270+
* Gets the usage fields for the supplied option.
271+
* @param option The option annotation.
272+
* @param f The field the annotation is attached to.
273+
* @param obj The parent options object, used to access the default value for this field.
274+
* @param type The type string used in the usage (may be the enum constants, or a short type descriptor).
275+
* @return The fields for the usage string.
276+
*/
214277
public static ArrayList<String> getOptionUsage(Option option, Field f, Options obj, String type) {
215278
ArrayList<String> output = new ArrayList<>();
216279
if (option.charName() != Option.EMPTY_CHAR) {

olcut-core/src/test/java/com/oracle/labs/mlrg/olcut/config/OptionsTest.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.nio.file.Path;
3333
import java.util.ArrayList;
3434
import java.util.Arrays;
35+
import java.util.EnumSet;
3536
import java.util.List;
3637
import java.util.Random;
3738

@@ -42,7 +43,7 @@
4243

4344
public class OptionsTest {
4445

45-
@Test
46+
@Test
4647
public void testBasic() {
4748
String[] args = new String[] {"--deeper-string","How low can you go?",
4849
"--deep-string", "double bass",
@@ -152,6 +153,22 @@ public void execute() throws Throwable {
152153
assertTrue(e.getMessage().contains("no default constructor"));
153154

154155
}
156+
157+
@Test
158+
public void enumSetUsage() {
159+
EnumSetOptions opts = new EnumSetOptions();
160+
161+
String[] args = new String[]{"--enum-set", "THINGS,STUFF"};
162+
163+
ConfigurationManager cm = new ConfigurationManager(args, opts);
164+
165+
EnumSet<OptionsEnum> enumSet = EnumSet.of(OptionsEnum.THINGS, OptionsEnum.STUFF);
166+
assertEquals(opts.enumSet,enumSet);
167+
168+
String usage = cm.usage();
169+
170+
assertTrue(usage.contains("EnumSet - {THINGS, STUFF, OTHER_THINGS}"));
171+
}
155172
}
156173

157174
class TestOptions implements Options {
@@ -251,6 +268,20 @@ public String toString() {
251268
}
252269
}
253270

271+
class EnumSetOptions implements Options {
272+
@Override
273+
public String getOptionsDescription() {
274+
return "It's got an enumset.";
275+
}
276+
@Option(charName='e', longName="enum-set", usage="It's an enum set")
277+
public EnumSet<OptionsEnum> enumSet;
278+
279+
@Override
280+
public String toString() {
281+
return "enumSet="+enumSet.toString();
282+
}
283+
}
284+
254285
enum OptionsEnum {
255286
THINGS, STUFF, OTHER_THINGS;
256287
}

0 commit comments

Comments
 (0)