/
ArgHandlerEnum.java
154 lines (133 loc) · 5.41 KB
/
ArgHandlerEnum.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
/*
* Copyright 2014 Google Inc.
*
* 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 com.google.gwt.util.tools;
import static com.google.gwt.thirdparty.guava.common.base.Preconditions.checkNotNull;
import com.google.gwt.thirdparty.guava.common.base.Ascii;
import com.google.gwt.thirdparty.guava.common.base.Enums;
import com.google.gwt.thirdparty.guava.common.base.Joiner;
import com.google.gwt.thirdparty.guava.common.base.Preconditions;
import com.google.gwt.thirdparty.guava.common.base.Predicate;
import com.google.gwt.thirdparty.guava.common.collect.FluentIterable;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
/**
* A generic arg handler for options defined by enums.
*
* @param <T> enum type providing option values.
*/
public abstract class ArgHandlerEnum<T extends Enum<T>> extends ArgHandler {
private final Class<T> optionsEnumClass;
private final T defaultValue;
private final boolean allowAbbreviation;
private static final int ABBREVIATION_MIN_SIZE = 3;
/**
* Constructor, default value must be handled by the user code.
*/
public ArgHandlerEnum(Class<T> optionsEnumClass) {
this(optionsEnumClass, null, false);
}
/**
* Constructor, allows to specify the default value for the option and whether to accept or
* prefixes as abbreviations.
* <p>
* When {@code defaultValue} is null, handling of the default for the option is left to be
* handled by the user code.
*/
public ArgHandlerEnum(
Class<T> optionsEnumClass, @Nullable T defaultValue, boolean allowAbbreviation) {
Preconditions.checkArgument(optionsEnumClass.getEnumConstants().length > 1);
this.optionsEnumClass = checkNotNull(optionsEnumClass);
this.defaultValue = defaultValue;
this.allowAbbreviation = allowAbbreviation;
}
@Override
public String[] getDefaultArgs() {
if (defaultValue == null) {
return null;
}
return new String[] { getTag(), defaultValue.name() };
}
@Override
public String[] getTagArgs() {
return new String[]{ "(" + getFormattedOptionNames("|", optionsEnumClass) + ")" };
}
@Override
public final int handle(String[] args, int startIndex) {
// A command line argument corresponding to an enum option has a parameter
// (the desired value), will be at startIndex + 1.
int optionValueIndex = startIndex + 1;
if (optionValueIndex < args.length) {
String value = Ascii.toUpperCase(args[optionValueIndex].trim());
T mode = matchOption(value);
if (mode == null) {
System.err.format("%s is not a valid option for %s\n", value, getTag());
System.err.format("%s value must be one of %s.\n",
getTag(), getFormattedOptionNames(", ", " or ", optionsEnumClass));
return -1;
}
setValue(mode);
return 1;
}
System.err.format("Missing argument for %s must be followed by one of %s.\n" +
getTag(), getFormattedOptionNames(", ", " or ", optionsEnumClass));
return -1;
}
protected String getPurposeString(String prefix) {
String maybeExperimentalString = isExperimental() ? "EXPERIMENTAL: " : "";
String defaultValueString = defaultValue != null ?
" (defaults to " + defaultValue.name() + ")" : "";
return String.format("%s%s %s%s", maybeExperimentalString, prefix,
getFormattedOptionNames(", ", " or ", optionsEnumClass), defaultValueString);
}
/**
* Override to handle the setting of an enum value.
*/
public abstract void setValue(T value);
private List<String> getEnumNames(Class<T> optionsEnumClass) {
return FluentIterable.from(Arrays.asList(optionsEnumClass.getEnumConstants()))
.transform(Enums.stringConverter(optionsEnumClass).reverse())
.toList();
}
private String getFormattedOptionNames(String separator, Class<T> optionsEnumClass) {
return getFormattedOptionNames(separator, separator, optionsEnumClass);
}
private String getFormattedOptionNames(
String separator, String lastSeparator, Class<T> optionsEnumClass) {
List<String> enumNames = getEnumNames(optionsEnumClass);
List<String> allNamesButLast = enumNames.subList(0, enumNames.size() - 1);
String lastName = enumNames.get(enumNames.size() - 1);
return Joiner.on(separator).join(allNamesButLast) + lastSeparator + lastName;
}
private Predicate<Enum<?>> buildMatchPredicate(final String value) {
return
new Predicate<Enum<?>>() {
@Override
public boolean apply(Enum<?> t) {
if (allowAbbreviation && value.length() >= ABBREVIATION_MIN_SIZE) {
return t.name().startsWith(value);
}
return t.name().equals(value);
}
};
}
private T matchOption(String value) {
List<T> matchedOptions = FluentIterable.from(Arrays.asList(optionsEnumClass.getEnumConstants()))
.filter(buildMatchPredicate(value))
.toList();
return matchedOptions.size() == 1 ? matchedOptions.get(0) : null;
}
}