package org.gambi.tapestry5.cli;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.validation.Validator;
import org.apache.tapestry5.beanvalidator.BeanValidatorConfigurer;
import org.apache.tapestry5.beanvalidator.BeanValidatorSource;
import org.apache.tapestry5.ioc.ObjectLocator;
import org.apache.tapestry5.ioc.ObjectProvider;
import org.apache.tapestry5.ioc.OrderedConfiguration;
import org.apache.tapestry5.ioc.ServiceBinder;
import org.apache.tapestry5.ioc.annotations.Contribute;
import org.apache.tapestry5.ioc.annotations.InjectService;
import org.apache.tapestry5.ioc.annotations.SubModule;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.ioc.services.Builtin;
import org.apache.tapestry5.ioc.services.MasterObjectProvider;
import org.apache.tapestry5.ioc.services.PipelineBuilder;
import org.gambi.tapestry5.cli.data.CLIOption;
import org.gambi.tapestry5.cli.modules.AdditionalCoercions;
import org.gambi.tapestry5.cli.services.CLIOptionProvider;
import org.gambi.tapestry5.cli.services.CLIOptionSource;
import org.gambi.tapestry5.cli.services.CLIParser;
import org.gambi.tapestry5.cli.services.CLIValidator;
import org.gambi.tapestry5.cli.services.CLIValidatorFilter;
import org.gambi.tapestry5.cli.services.impl.CLIOptionSourceImpl;
import org.gambi.tapestry5.cli.services.impl.CLIParserImpl;
import org.gambi.tapestry5.cli.services.impl.CLIValidatorFilterImpl;
import org.gambi.tapestry5.cli.services.impl.DefaullCLIValidatorFilter;
import org.gambi.tapestry5.cli.services.impl.TapestryConstraintValidatorFactory;
import org.gambi.tapestry5.cli.services.internal.ApplicationConfigurationSource;
import org.gambi.tapestry5.cli.services.internal.BridgeCLIOptionProvider;
import org.gambi.tapestry5.cli.services.internal.CLIInputObjectProvider;
import org.gambi.tapestry5.cli.services.internal.CLIOptionObjectProvider;
import org.gambi.tapestry5.cli.services.internal.impl.ApplicationConfigurationSourceImpl;
import org.gambi.tapestry5.cli.services.internal.impl.BridgeCLIOptionProviderImpl;
import org.gambi.tapestry5.cli.utils.CLISymbolConstants;
import org.slf4j.Logger;
/*
* References : -
* http://blog.tapestry5.de/index.php/2010/01/04/tapestry-and-jsr-
* 303-bean-validation-api/ -
* http://tawus.wordpress.com/2011/05/12/tapestry-magic
* -12-tapestry-ioc-aware-jsr-303-custom-validators/
*
*
*/
/**
* A T5 enable module for CLI argument parsing.
*
* The module provide a CLI-Parser and CLI-validation framework to easy the
* definition and operation of T5 enable applications that rely on inputs, and
* options to be specified via the command line.
*
* The input arguments are parsed, then validated, and eventually "exported" as
* symbols to be used inside the application.
*
* Inputs and Options are exported via the CLIOptionSource service
*
* @author alessiogambi
*
* @category Module
*/
@SubModule({ AdditionalCoercions.class })
public class CLIModule {
/**
* Auto build services
*
* @category AutoBuild ApplicationConfigurationSource CLIOptionSource
*/
@SuppressWarnings("unchecked")
public static void bind(final ServiceBinder binder) {
binder.bind(ApplicationConfigurationSource.class,
ApplicationConfigurationSourceImpl.class);
/*
* Note that we MUST mark this with the Builtin annotation
*/
binder.bind(CLIOptionSource.class, CLIOptionSourceImpl.class)
.withMarker(Builtin.class);
}
/**
* Build the CLI Parser object to process the input data.
*
* @param logger
* @param applicationConfigurationSource
* @param validator
* @param options
* @return CLIParser
*
* @category Build CLIParser
*/
public CLIParser buildCLIParser(
/**
* @category Resource
*/
Logger logger,
/**
* @category Symbol UserContributions
*/
@Symbol(CLISymbolConstants.COMMAND_NAME) String commandName,
/**
* @category Service CLIModule
*/
ApplicationConfigurationSource applicationConfigurationSource,
/**
* @category Service SimpleValidatorModule
*/
Validator validator,
/**
* @category Service CLIValidator
*/
CLIValidator cliValidator,
/**
* @category Service BridgeCLIOptionProvider
*/
BridgeCLIOptionProvider bridgeCLIOptionProvider,
/**
* @category UserContributions
*/
Collection<CLIOption> options) {
return new CLIParserImpl(logger, applicationConfigurationSource,
validator, cliValidator, commandName, bridgeCLIOptionProvider,
options);
}
/**
* Build the second validation layer as a sequence of CLIValidators
*
*
* @param commands
* @param chainBuilder
* @return
*
* @category Build CLIValidator
*/
public static CLIValidator build(
/**
* @category Resource
*/
Logger logger,
/**
* Add the CLIOption and CLIInput annotation provider. This code is taken
* from {@link TapestryIOCModule}.
*
* <dl>
* <dt>CLIOption</dt>
* <dd>Supports the {@link org.gambi.tapestry5.cli.annotations.CLIOption}
* annotations</dd>
* <dt>CLIInput</dt>
* <dd>Supports the {@link org.gambi.tapestry5.cli.annotations.CLIInput}
* annotations</dd>
* </dl>
*/
PipelineBuilder pipe,
/**
* @category UserContributions
*/
List<CLIValidator> contributions) {
/*
* This is a trick to force a specific sequence of filters: we use the
* contributions to define an standard class that acts as the filter
*/
List<CLIValidatorFilter> filters = new ArrayList<CLIValidatorFilter>();
for (CLIValidator cliValidator : contributions) {
filters.add(new CLIValidatorFilterImpl(logger, cliValidator));
}
// Define the terminator filter
filters.add(new DefaullCLIValidatorFilter());
return pipe.build(logger, CLIValidator.class, CLIValidatorFilter.class,
filters);
}
/**
* Contribute the CLIOption/CLIInput injection services
*
* @param configuration
*
* @category UserContributions MasterObjectProvider
*/
@Contribute(MasterObjectProvider.class)
public static void setupObjectProviders(
OrderedConfiguration<ObjectProvider> configuration) {
configuration.addInstance("CLIOption", CLIOptionObjectProvider.class,
"before:AnnotationBasedContributions");
configuration.addInstance("CLIInput", CLIInputObjectProvider.class,
"before:AnnotationBasedContributions");
}
/**
* Contribute the CLIOption/CLIInput service providers
*
* @param providers
*
* @category UserContributions CLIOptionSource
*/
@Contribute(CLIOptionSource.class)
public static void setupCLIOptionProviders(
@InjectService("BridgeCLIOptionProvider") CLIOptionProvider bridgeCLIOptionProvider,
OrderedConfiguration<CLIOptionProvider> providers) {
providers.add("Bridge", bridgeCLIOptionProvider, "");
}
/**
* Contribute the simple validation with tapestry5 enabled validators. this
* solution is taken from:
* http://tawus.wordpress.com/2011/05/12/tapestry-magic
* -12-tapestry-ioc-aware-jsr-303-custom-validators/
*
* @param locator
* @param threadLocale
* @param configuration
*
* @category UserContributions BeanValidatorSource
*/
@Contribute(BeanValidatorSource.class)
public static void contributeBeanValidatorSource(
final OrderedConfiguration<BeanValidatorConfigurer> configuration,
final ObjectLocator locator) {
configuration.add("TapestryEnabledValidationConstraints",
new BeanValidatorConfigurer() {
public void configure(
javax.validation.Configuration<?> configuration) {
configuration
.constraintValidatorFactory(new TapestryConstraintValidatorFactory(
locator));
}
});
}
/**
* Build the internal service that acts as bridge to carry around validated
* input
*
* @return
*
* @category Build BridgeCLIOptionProvider
*/
public BridgeCLIOptionProvider build() {
return new BridgeCLIOptionProviderImpl();
}
}