/**
* 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.apache.aurora.common.args;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.reflect.TypeToken;
import org.apache.aurora.common.args.apt.Configuration;
import org.apache.aurora.common.args.apt.Configuration.ParserInfo;
import static com.google.common.base.Preconditions.checkArgument;
import static org.apache.aurora.common.args.apt.Configuration.ConfigurationException;
/**
* A registry of Parsers for different supported argument types.
*
* @author William Farner
*/
public final class Parsers implements ParserOracle {
public static final Splitter MULTI_VALUE_SPLITTER =
Splitter.on(",").trimResults().omitEmptyStrings();
private static final Function<ParserInfo, Class<?>> INFO_TO_PARSED_TYPE =
parserInfo -> {
try {
return Class.forName(parserInfo.parsedType);
} catch (ClassNotFoundException e) {
throw new ConfigurationException(e);
}
};
@VisibleForTesting
static final Function<ParserInfo, Parser<?>> INFO_TO_PARSER =
parserInfo -> {
try {
Class<?> parserClass = Class.forName(parserInfo.parserClass);
Constructor<?> constructor = parserClass.getDeclaredConstructor();
constructor.setAccessible(true);
return (Parser<?>) constructor.newInstance();
} catch (ClassNotFoundException e) {
throw new ConfigurationException(e);
} catch (InstantiationException e) {
throw new ConfigurationException(e);
} catch (IllegalAccessException e) {
throw new ConfigurationException(e);
} catch (NoSuchMethodException e) {
throw new ConfigurationException(e);
} catch (InvocationTargetException e) {
throw new ConfigurationException(e);
}
};
private final ImmutableMap<Class<?>, Parser<?>> registry;
/**
* Creates a new parser registry over the specified {@code parsers}.
*
* @param parsers The parsers to register.
*/
public Parsers(Map<Class<?>, Parser<?>> parsers) {
Preconditions.checkNotNull(parsers);
registry = ImmutableMap.copyOf(parsers);
}
@Override
public <T> Parser<T> get(TypeToken<T> type) throws IllegalArgumentException {
Parser<?> parser;
Class<?> explicitClass = type.getRawType();
while (((parser = registry.get(explicitClass)) == null) && (explicitClass != null)) {
explicitClass = explicitClass.getSuperclass();
}
checkArgument(parser != null, "No parser found for " + type);
// We control loading of the registry which ensures a proper mapping of class -> parser
@SuppressWarnings("unchecked")
Parser<T> parserT = (Parser<T>) parser;
return parserT;
}
static Parsers fromConfiguration(Configuration configuration) {
Map<Class<?>, Parser<?>> parsers =
Maps.transformValues(
Maps.uniqueIndex(configuration.parserInfo(), INFO_TO_PARSED_TYPE),
INFO_TO_PARSER);
return new Parsers(parsers);
}
}