1
18
19 package org.xnio;
20
21 import java.io.InvalidObjectException;
22 import java.io.ObjectStreamException;
23 import java.io.Serializable;
24 import java.lang.reflect.Field;
25 import java.lang.reflect.Modifier;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.LinkedHashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 import static org.xnio._private.Messages.msg;
36
37
43 public abstract class Option<T> implements Serializable {
44
45 private static final long serialVersionUID = -1564427329140182760L;
46
47 private final Class<?> declClass;
48 private final String name;
49
50 Option(final Class<?> declClass, final String name) {
51 if (declClass == null) {
52 throw msg.nullParameter("declClass");
53 }
54 if (name == null) {
55 throw msg.nullParameter("name");
56 }
57 this.declClass = declClass;
58 this.name = name;
59 }
60
61
70 public static <T> Option<T> simple(final Class<?> declClass, final String name, final Class<T> type) {
71 return new SingleOption<T>(declClass, name, type);
72 }
73
74
83 public static <T> Option<Sequence<T>> sequence(final Class<?> declClass, final String name, final Class<T> elementType) {
84 return new SequenceOption<T>(declClass, name, elementType);
85 }
86
87
96 public static <T> Option<Class<? extends T>> type(final Class<?> declClass, final String name, final Class<T> declType) {
97 return new TypeOption<T>(declClass, name, declType);
98 }
99
100
109 public static <T> Option<Sequence<Class<? extends T>>> typeSequence(final Class<?> declClass, final String name, final Class<T> elementDeclType) {
110 return new TypeSequenceOption<T>(declClass, name, elementDeclType);
111 }
112
113
118 public String getName() {
119 return name;
120 }
121
122
127 public String toString() {
128 return declClass.getName() + "." + name;
129 }
130
131
140 public static Option<?> fromString(String name, ClassLoader classLoader) throws IllegalArgumentException {
141 final int lastDot = name.lastIndexOf('.');
142 if (lastDot == -1) {
143 throw msg.invalidOptionName(name);
144 }
145 final String fieldName = name.substring(lastDot + 1);
146 final String className = name.substring(0, lastDot);
147 final Class<?> clazz;
148 try {
149 clazz = Class.forName(className, true, classLoader);
150 } catch (ClassNotFoundException e) {
151 throw msg.optionClassNotFound(className, classLoader);
152 }
153 final Field field;
154 try {
155 field = clazz.getField(fieldName);
156 } catch (NoSuchFieldException e) {
157 throw msg.noField(fieldName, clazz);
158 }
159 final int modifiers = field.getModifiers();
160 if (! Modifier.isPublic(modifiers)) {
161 throw msg.fieldNotAccessible(fieldName, clazz);
162 }
163 if (! Modifier.isStatic(modifiers)) {
164 throw msg.fieldNotStatic(fieldName, clazz);
165 }
166 final Option<?> option;
167 try {
168 option = (Option<?>) field.get(null);
169 } catch (IllegalAccessException e) {
170 throw msg.fieldNotAccessible(fieldName, clazz);
171 }
172 if (option == null) {
173 throw msg.invalidNullOption(name);
174 }
175 return option;
176 }
177
178
185 public abstract T cast(Object o) throws ClassCastException;
186
187
197 public final T cast(Object o, T defaultVal) throws ClassCastException {
198 return o == null ? defaultVal : cast(o);
199 }
200
201
209 public abstract T parseValue(String string, ClassLoader classLoader) throws IllegalArgumentException;
210
211
217 protected final Object readResolve() throws ObjectStreamException {
218 try {
219 final Field field = declClass.getField(name);
220 final int modifiers = field.getModifiers();
221 if (! Modifier.isPublic(modifiers)) {
222 throw new InvalidObjectException("Invalid Option instance (the field is not public)");
223 }
224 if (! Modifier.isStatic(modifiers)) {
225 throw new InvalidObjectException("Invalid Option instance (the field is not static)");
226 }
227 final Option<?> option = (Option<?>) field.get(null);
228 if (option == null) {
229 throw new InvalidObjectException("Invalid null Option");
230 }
231 return option;
232 } catch (NoSuchFieldException e) {
233 throw new InvalidObjectException("Invalid Option instance (no matching field)");
234 } catch (IllegalAccessException e) {
235 throw new InvalidObjectException("Invalid Option instance (Illegal access on field get)");
236 }
237 }
238
239
244 public static Option.SetBuilder setBuilder() {
245 return new Option.SetBuilder();
246 }
247
248
251 public static class SetBuilder {
252 private List<Option<?>> optionSet = new ArrayList<Option<?>>();
253
254 SetBuilder() {
255 }
256
257
263 public Option.SetBuilder add(Option<?> option) {
264 if (option == null) {
265 throw msg.nullParameter("option");
266 }
267 optionSet.add(option);
268 return this;
269 }
270
271
278 public Option.SetBuilder add(Option<?> option1, Option<?> option2) {
279 if (option1 == null) {
280 throw msg.nullParameter("option1");
281 }
282 if (option2 == null) {
283 throw msg.nullParameter("option2");
284 }
285 optionSet.add(option1);
286 optionSet.add(option2);
287 return this;
288 }
289
290
298 public Option.SetBuilder add(Option<?> option1, Option<?> option2, Option<?> option3) {
299 if (option1 == null) {
300 throw msg.nullParameter("option1");
301 }
302 if (option2 == null) {
303 throw msg.nullParameter("option2");
304 }
305 if (option3 == null) {
306 throw msg.nullParameter("option3");
307 }
308 optionSet.add(option1);
309 optionSet.add(option2);
310 optionSet.add(option3);
311 return this;
312 }
313
314
320 public Option.SetBuilder add(Option<?>... options) {
321 if (options == null) {
322 throw msg.nullParameter("options");
323 }
324 for (Option<?> option : options) {
325 add(option);
326 }
327 return this;
328 }
329
330
336 public Option.SetBuilder addAll(Collection<Option<?>> options) {
337 if (options == null) {
338 throw msg.nullParameter("option");
339 }
340 for (Option<?> option : options) {
341 add(option);
342 }
343 return this;
344 }
345
346
351 public Set<Option<?>> create() {
352 return Collections.unmodifiableSet(new LinkedHashSet<Option<?>>(optionSet));
353 }
354 }
355
356 interface ValueParser<T> {
357 T parseValue(String string, ClassLoader classLoader) throws IllegalArgumentException;
358 }
359
360 private static final Map<Class<?>, Option.ValueParser<?>> parsers;
361
362 private static final Option.ValueParser<?> noParser = new Option.ValueParser<Object>() {
363 public Object parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException {
364 throw msg.noOptionParser();
365 }
366 };
367
368 static {
369 final Map<Class<?>, Option.ValueParser<?>> map = new HashMap<Class<?>, Option.ValueParser<?>>();
370 map.put(Byte.class, new Option.ValueParser<Byte>() {
371 public Byte parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException {
372 return Byte.decode(string.trim());
373 }
374 });
375 map.put(Short.class, new Option.ValueParser<Short>() {
376 public Short parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException {
377 return Short.decode(string.trim());
378 }
379 });
380 map.put(Integer.class, new Option.ValueParser<Integer>() {
381 public Integer parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException {
382 return Integer.decode(string.trim());
383 }
384 });
385 map.put(Long.class, new Option.ValueParser<Long>() {
386 public Long parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException {
387 return Long.decode(string.trim());
388 }
389 });
390 map.put(String.class, new Option.ValueParser<String>() {
391 public String parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException {
392 return string.trim();
393 }
394 });
395 map.put(Boolean.class, new Option.ValueParser<Boolean>() {
396 public Boolean parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException {
397 return Boolean.valueOf(string.trim());
398 }
399 });
400 map.put(Property.class, new Option.ValueParser<Object>() {
401 public Object parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException {
402 final int idx = string.indexOf('=');
403 if (idx == -1) {
404 throw msg.invalidOptionPropertyFormat(string);
405 }
406 return Property.of(string.substring(0, idx), string.substring(idx + 1, string.length()));
407 }
408 });
409 parsers = map;
410 }
411
412 static <T> Option.ValueParser<Class<? extends T>> getClassParser(final Class<T> argType) {
413 return new ValueParser<Class<? extends T>>() {
414 public Class<? extends T> parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException {
415 try {
416 return Class.forName(string, false, classLoader).asSubclass(argType);
417 } catch (ClassNotFoundException e) {
418 throw msg.classNotFound(string, e);
419 } catch (ClassCastException e) {
420 throw msg.classNotInstance(string, argType);
421 }
422 }
423 };
424 }
425
426 static <T, E extends Enum<E>> Option.ValueParser<T> getEnumParser(final Class<T> enumType) {
427 return new ValueParser<T>() {
428 public T parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException {
429 return enumType.cast(Enum.<E>valueOf(asEnum(enumType), string.trim()));
430 }
431 };
432 }
433
434 @SuppressWarnings("unchecked")
435 private static <T, E extends Enum<E>> Class<E> asEnum(final Class<T> enumType) {
436 return (Class<E>) enumType;
437 }
438
439 @SuppressWarnings("unchecked")
440 static <T> Option.ValueParser<T> getParser(final Class<T> argType) {
441 if (argType.isEnum()) {
442 return getEnumParser(argType);
443 } else {
444 final Option.ValueParser<?> value = parsers.get(argType);
445 return (Option.ValueParser<T>) (value == null ? noParser : value);
446 }
447 }
448 }
449
450