// =================================================================================================
// Copyright 2011 Twitter, Inc.
// -------------------------------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this work except in compliance with the License.
// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args;
import java.util.Map;
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 com.twitter.common.args.apt.Configuration;
import com.twitter.common.args.apt.Configuration.ParserInfo;
import static com.google.common.base.Preconditions.checkArgument;
import static com.twitter.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 =
new Function<ParserInfo, Class<?>>() {
@Override public Class<?> apply(ParserInfo parserInfo) {
try {
return Class.forName(parserInfo.parsedType);
} catch (ClassNotFoundException e) {
throw new ConfigurationException(e);
}
}
};
private static final Function<ParserInfo, Parser<?>> INFO_TO_PARSER =
new Function<ParserInfo, Parser<?>>() {
@Override public Parser apply(ParserInfo parserInfo) {
try {
return (Parser<?>) Class.forName(parserInfo.parserClass).newInstance();
} catch (ClassNotFoundException e) {
throw new ConfigurationException(e);
} catch (InstantiationException e) {
throw new ConfigurationException(e);
} catch (IllegalAccessException 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;
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);
}
}