/*
 * JBoss, Home of Professional Open Source
 *
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * 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 org.xnio;

import java.io.InvalidObjectException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.xnio._private.Messages.msg;

A strongly-typed option to configure an aspect of a service or connection. Options are immutable and use identity comparisons and hash codes. Options should always be declared as public static final members in order to support serialization.
Type parameters:
  • <T> – the option value type
/** * A strongly-typed option to configure an aspect of a service or connection. Options are immutable and use identity comparisons * and hash codes. Options should always be declared as <code>public static final</code> members in order to support serialization. * * @param <T> the option value type */
public abstract class Option<T> implements Serializable { private static final long serialVersionUID = -1564427329140182760L; private final Class<?> declClass; private final String name; Option(final Class<?> declClass, final String name) { if (declClass == null) { throw msg.nullParameter("declClass"); } if (name == null) { throw msg.nullParameter("name"); } this.declClass = declClass; this.name = name; }
Create an option with a simple type. The class object given must represent some immutable type, otherwise unexpected behavior may result.
Params:
  • declClass – the declaring class of the option
  • name – the (field) name of this option
  • type – the class of the value associated with this option
Returns:the option instance
/** * Create an option with a simple type. The class object given <b>must</b> represent some immutable type, otherwise * unexpected behavior may result. * * @param declClass the declaring class of the option * @param name the (field) name of this option * @param type the class of the value associated with this option * @return the option instance */
public static <T> Option<T> simple(final Class<?> declClass, final String name, final Class<T> type) { return new SingleOption<T>(declClass, name, type); }
Create an option with a sequence type. The class object given must represent some immutable type, otherwise unexpected behavior may result.
Params:
  • declClass – the declaring class of the option
  • name – the (field) name of this option
  • elementType – the class of the sequence element value associated with this option
Returns:the option instance
/** * Create an option with a sequence type. The class object given <b>must</b> represent some immutable type, otherwise * unexpected behavior may result. * * @param declClass the declaring class of the option * @param name the (field) name of this option * @param elementType the class of the sequence element value associated with this option * @return the option instance */
public static <T> Option<Sequence<T>> sequence(final Class<?> declClass, final String name, final Class<T> elementType) { return new SequenceOption<T>(declClass, name, elementType); }
Create an option with a class type. The class object given may represent any type.
Params:
  • declClass – the declaring class of the option
  • name – the (field) name of this option
  • declType – the class object for the type of the class object given
Type parameters:
  • <T> – the type of the class object given
Returns:the option instance
/** * Create an option with a class type. The class object given may represent any type. * * @param declClass the declaring class of the option * @param name the (field) name of this option * @param declType the class object for the type of the class object given * @param <T> the type of the class object given * @return the option instance */
public static <T> Option<Class<? extends T>> type(final Class<?> declClass, final String name, final Class<T> declType) { return new TypeOption<T>(declClass, name, declType); }
Create an option with a sequence-of-types type. The class object given may represent any type.
Params:
  • declClass – the declaring class of the option
  • name – the (field) name of this option
  • elementDeclType – the class object for the type of the sequence element class object given
Type parameters:
  • <T> – the type of the sequence element class object given
Returns:the option instance
/** * Create an option with a sequence-of-types type. The class object given may represent any type. * * @param declClass the declaring class of the option * @param name the (field) name of this option * @param elementDeclType the class object for the type of the sequence element class object given * @param <T> the type of the sequence element class object given * @return the option instance */
public static <T> Option<Sequence<Class<? extends T>>> typeSequence(final Class<?> declClass, final String name, final Class<T> elementDeclType) { return new TypeSequenceOption<T>(declClass, name, elementDeclType); }
Get the name of this option.
Returns:the option name
/** * Get the name of this option. * * @return the option name */
public String getName() { return name; }
Get a human-readable string representation of this object.
Returns:the string representation
/** * Get a human-readable string representation of this object. * * @return the string representation */
public String toString() { return declClass.getName() + "." + name; }
Get an option from a string name, using the given classloader. If the classloader is null, the bootstrap classloader will be used.
Params:
  • name – the option string
  • classLoader – the class loader
Throws:
Returns:the option
/** * Get an option from a string name, using the given classloader. If the classloader is {@code null}, the bootstrap * classloader will be used. * * @param name the option string * @param classLoader the class loader * @return the option * @throws IllegalArgumentException if the given option name is not valid */
public static Option<?> fromString(String name, ClassLoader classLoader) throws IllegalArgumentException { final int lastDot = name.lastIndexOf('.'); if (lastDot == -1) { throw msg.invalidOptionName(name); } final String fieldName = name.substring(lastDot + 1); final String className = name.substring(0, lastDot); final Class<?> clazz; try { clazz = Class.forName(className, true, classLoader); } catch (ClassNotFoundException e) { throw msg.optionClassNotFound(className, classLoader); } final Field field; try { field = clazz.getField(fieldName); } catch (NoSuchFieldException e) { throw msg.noField(fieldName, clazz); } final int modifiers = field.getModifiers(); if (! Modifier.isPublic(modifiers)) { throw msg.fieldNotAccessible(fieldName, clazz); } if (! Modifier.isStatic(modifiers)) { throw msg.fieldNotStatic(fieldName, clazz); } final Option<?> option; try { option = (Option<?>) field.get(null); } catch (IllegalAccessException e) { throw msg.fieldNotAccessible(fieldName, clazz); } if (option == null) { throw msg.invalidNullOption(name); } return option; }
Return the given object as the type of this option. If the cast could not be completed, an exception is thrown.
Params:
  • o – the object to cast
Throws:
Returns:the cast object
/** * Return the given object as the type of this option. If the cast could not be completed, an exception is thrown. * * @param o the object to cast * @return the cast object * @throws ClassCastException if the object is not of a compatible type */
public abstract T cast(Object o) throws ClassCastException;
Return the given object as the type of this option. If the cast could not be completed, an exception is thrown.
Params:
  • o – the object to cast
  • defaultVal – the value to return if o is null
Throws:
Returns:the cast object
/** * Return the given object as the type of this option. If the cast could not be completed, an exception is thrown. * * @param o the object to cast * @param defaultVal the value to return if {@code o} is {@code null} * * @return the cast object * * @throws ClassCastException if the object is not of a compatible type */
public final T cast(Object o, T defaultVal) throws ClassCastException { return o == null ? defaultVal : cast(o); }
Parse a string value for this option.
Params:
  • string – the string
  • classLoader – the class loader to use to parse the value
Throws:
Returns:the parsed value
/** * Parse a string value for this option. * * @param string the string * @param classLoader the class loader to use to parse the value * @return the parsed value * @throws IllegalArgumentException if the argument could not be parsed */
public abstract T parseValue(String string, ClassLoader classLoader) throws IllegalArgumentException;
Resolve this instance for serialization.
Throws:
Returns:the resolved object
/** * Resolve this instance for serialization. * * @return the resolved object * @throws java.io.ObjectStreamException if the object could not be resolved */
protected final Object readResolve() throws ObjectStreamException { try { final Field field = declClass.getField(name); final int modifiers = field.getModifiers(); if (! Modifier.isPublic(modifiers)) { throw new InvalidObjectException("Invalid Option instance (the field is not public)"); } if (! Modifier.isStatic(modifiers)) { throw new InvalidObjectException("Invalid Option instance (the field is not static)"); } final Option<?> option = (Option<?>) field.get(null); if (option == null) { throw new InvalidObjectException("Invalid null Option"); } return option; } catch (NoSuchFieldException e) { throw new InvalidObjectException("Invalid Option instance (no matching field)"); } catch (IllegalAccessException e) { throw new InvalidObjectException("Invalid Option instance (Illegal access on field get)"); } }
Create a builder for an immutable option set.
Returns:the builder
/** * Create a builder for an immutable option set. * * @return the builder */
public static Option.SetBuilder setBuilder() { return new Option.SetBuilder(); }
A builder for an immutable option set.
/** * A builder for an immutable option set. */
public static class SetBuilder { private List<Option<?>> optionSet = new ArrayList<Option<?>>(); SetBuilder() { }
Add an option to this set.
Params:
  • option – the option to add
Returns:this builder
/** * Add an option to this set. * * @param option the option to add * @return this builder */
public Option.SetBuilder add(Option<?> option) { if (option == null) { throw msg.nullParameter("option"); } optionSet.add(option); return this; }
Add options to this set.
Params:
  • option1 – the first option to add
  • option2 – the second option to add
Returns:this builder
/** * Add options to this set. * * @param option1 the first option to add * @param option2 the second option to add * @return this builder */
public Option.SetBuilder add(Option<?> option1, Option<?> option2) { if (option1 == null) { throw msg.nullParameter("option1"); } if (option2 == null) { throw msg.nullParameter("option2"); } optionSet.add(option1); optionSet.add(option2); return this; }
Add options to this set.
Params:
  • option1 – the first option to add
  • option2 – the second option to add
  • option3 – the third option to add
Returns:this builder
/** * Add options to this set. * * @param option1 the first option to add * @param option2 the second option to add * @param option3 the third option to add * @return this builder */
public Option.SetBuilder add(Option<?> option1, Option<?> option2, Option<?> option3) { if (option1 == null) { throw msg.nullParameter("option1"); } if (option2 == null) { throw msg.nullParameter("option2"); } if (option3 == null) { throw msg.nullParameter("option3"); } optionSet.add(option1); optionSet.add(option2); optionSet.add(option3); return this; }
Add options to this set.
Params:
  • options – the options to add
Returns:this builder
/** * Add options to this set. * * @param options the options to add * @return this builder */
public Option.SetBuilder add(Option<?>... options) { if (options == null) { throw msg.nullParameter("options"); } for (Option<?> option : options) { add(option); } return this; }
Add all options from a collection to this set.
Params:
  • options – the options to add
Returns:this builder
/** * Add all options from a collection to this set. * * @param options the options to add * @return this builder */
public Option.SetBuilder addAll(Collection<Option<?>> options) { if (options == null) { throw msg.nullParameter("option"); } for (Option<?> option : options) { add(option); } return this; }
Create the immutable option set instance.
Returns:the option set
/** * Create the immutable option set instance. * * @return the option set */
public Set<Option<?>> create() { return Collections.unmodifiableSet(new LinkedHashSet<Option<?>>(optionSet)); } } interface ValueParser<T> { T parseValue(String string, ClassLoader classLoader) throws IllegalArgumentException; } private static final Map<Class<?>, Option.ValueParser<?>> parsers; private static final Option.ValueParser<?> noParser = new Option.ValueParser<Object>() { public Object parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException { throw msg.noOptionParser(); } }; static { final Map<Class<?>, Option.ValueParser<?>> map = new HashMap<Class<?>, Option.ValueParser<?>>(); map.put(Byte.class, new Option.ValueParser<Byte>() { public Byte parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException { return Byte.decode(string.trim()); } }); map.put(Short.class, new Option.ValueParser<Short>() { public Short parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException { return Short.decode(string.trim()); } }); map.put(Integer.class, new Option.ValueParser<Integer>() { public Integer parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException { return Integer.decode(string.trim()); } }); map.put(Long.class, new Option.ValueParser<Long>() { public Long parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException { return Long.decode(string.trim()); } }); map.put(String.class, new Option.ValueParser<String>() { public String parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException { return string.trim(); } }); map.put(Boolean.class, new Option.ValueParser<Boolean>() { public Boolean parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException { return Boolean.valueOf(string.trim()); } }); map.put(Property.class, new Option.ValueParser<Object>() { public Object parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException { final int idx = string.indexOf('='); if (idx == -1) { throw msg.invalidOptionPropertyFormat(string); } return Property.of(string.substring(0, idx), string.substring(idx + 1, string.length())); } }); parsers = map; } static <T> Option.ValueParser<Class<? extends T>> getClassParser(final Class<T> argType) { return new ValueParser<Class<? extends T>>() { public Class<? extends T> parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException { try { return Class.forName(string, false, classLoader).asSubclass(argType); } catch (ClassNotFoundException e) { throw msg.classNotFound(string, e); } catch (ClassCastException e) { throw msg.classNotInstance(string, argType); } } }; } static <T, E extends Enum<E>> Option.ValueParser<T> getEnumParser(final Class<T> enumType) { return new ValueParser<T>() { public T parseValue(final String string, final ClassLoader classLoader) throws IllegalArgumentException { return enumType.cast(Enum.<E>valueOf(asEnum(enumType), string.trim())); } }; } @SuppressWarnings("unchecked") private static <T, E extends Enum<E>> Class<E> asEnum(final Class<T> enumType) { return (Class<E>) enumType; } @SuppressWarnings("unchecked") static <T> Option.ValueParser<T> getParser(final Class<T> argType) { if (argType.isEnum()) { return getEnumParser(argType); } else { final Option.ValueParser<?> value = parsers.get(argType); return (Option.ValueParser<T>) (value == null ? noParser : value); } } }