/**
* Copyright@2011 wro4j
*/
package ro.isdc.wro.model.resource.processor.factory;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.imageio.spi.ServiceRegistry;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.isdc.wro.model.resource.processor.ResourcePostProcessor;
import ro.isdc.wro.model.resource.processor.ResourcePreProcessor;
import ro.isdc.wro.model.resource.processor.decorator.ExtensionsAwareProcessorDecorator;
import ro.isdc.wro.model.resource.processor.decorator.ProcessorDecorator;
import ro.isdc.wro.model.resource.processor.support.ProcessorProvider;
import ro.isdc.wro.model.resource.support.AbstractConfigurableMultipleStrategy;
/**
* A {@link ProcessorsFactory} implementation which is easy to configure using a {@link Properties} object.
*
* @author Alex Objelean
* @created 30 Jul 2011
* @since 1.4.0
*/
public class ConfigurableProcessorsFactory
implements ProcessorsFactory {
private static final Logger LOG = LoggerFactory.getLogger(ConfigurableProcessorsFactory.class);
/**
* Property name to specify pre processors.
*/
public static final String PARAM_PRE_PROCESSORS = "preProcessors";
/**
* Property name to specify post processors.
*/
public static final String PARAM_POST_PROCESSORS = "postProcessors";
private Map<String, ResourcePreProcessor> preProcessorsMap;
private Map<String, ResourcePostProcessor> postProcessorsMap;
/**
* Holds the configurations describing available processors.
*/
private Properties properties;
private final AbstractConfigurableMultipleStrategy<ResourcePreProcessor, ProcessorProvider> configurablePreProcessors = createConfigurablePreProcessorsStrategy();
private final AbstractConfigurableMultipleStrategy<ResourcePostProcessor, ProcessorProvider> configurablePostProcessors = createConfigurablePostProcessorsStrategy();
/**
* @return the {@link AbstractConfigurableMultipleStrategy} responsible for providing postProcessors strategies.
*/
private AbstractConfigurableMultipleStrategy<ResourcePostProcessor, ProcessorProvider> createConfigurablePostProcessorsStrategy() {
return new AbstractConfigurableMultipleStrategy<ResourcePostProcessor, ProcessorProvider>() {
@Override
protected String getStrategyKey() {
return PARAM_POST_PROCESSORS;
}
@Override
protected void overrideDefaultStrategyMap(final Map<String, ResourcePostProcessor> map) {
copyAll(ConfigurableProcessorsFactory.this.getPostProcessorsMap(), map);
}
@Override
protected Map<String, ResourcePostProcessor> getStrategies(final ProcessorProvider provider) {
return getPostProcessorStrategies(provider);
}
@Override
protected ResourcePostProcessor getStrategyForAlias(final String alias) {
ResourcePostProcessor processor = super.getStrategyForAlias(alias);
if (processor == null) {
final String extension = FilenameUtils.getExtension(alias);
boolean hasExtension = !StringUtils.isEmpty(extension);
if (hasExtension) {
final String processorName = FilenameUtils.getBaseName(alias);
LOG.debug("processorName: {}", processorName);
processor = super.getStrategyForAlias(processorName);
if (processor != null) {
LOG.debug("adding Extension: {}", extension);
processor = ExtensionsAwareProcessorDecorator.decorate(new ProcessorDecorator(processor)).addExtension(
extension);
}
}
}
return processor;
}
@Override
protected Properties newProperties() {
return ConfigurableProcessorsFactory.this.getProperties();
}
};
}
/**
* @return the {@link AbstractConfigurableMultipleStrategy} responsible for providing preProcessors strategies.
*/
private AbstractConfigurableMultipleStrategy<ResourcePreProcessor, ProcessorProvider> createConfigurablePreProcessorsStrategy() {
return new AbstractConfigurableMultipleStrategy<ResourcePreProcessor, ProcessorProvider>() {
@Override
protected String getStrategyKey() {
return PARAM_PRE_PROCESSORS;
}
@Override
protected void overrideDefaultStrategyMap(final Map<String, ResourcePreProcessor> map) {
copyAll(ConfigurableProcessorsFactory.this.getPreProcessorsMap(), map);
}
@Override
protected Map<String, ResourcePreProcessor> getStrategies(final ProcessorProvider provider) {
return getPreProcessorStrategies(provider);
}
@Override
protected ResourcePreProcessor getStrategyForAlias(final String alias) {
ResourcePreProcessor processor = super.getStrategyForAlias(alias);
if (processor == null) {
final String extension = FilenameUtils.getExtension(alias);
boolean hasExtension = !StringUtils.isEmpty(extension);
if (hasExtension) {
final String processorName = FilenameUtils.getBaseName(alias);
LOG.debug("processorName: {}", processorName);
processor = super.getStrategyForAlias(processorName);
if (processor != null) {
LOG.debug("adding Extension: {}", extension);
processor = ExtensionsAwareProcessorDecorator.decorate(processor).addExtension(extension);
}
}
}
return processor;
}
@Override
protected Properties newProperties() {
return ConfigurableProcessorsFactory.this.getProperties();
};
};
}
/**
* @return default implementation of {@link Properties} containing the list of pre & post processors.
*/
protected Properties newProperties() {
return new Properties();
}
/**
* {@inheritDoc}
*/
public final Collection<ResourcePreProcessor> getPreProcessors() {
return configurablePreProcessors.getConfiguredStrategies();
}
/**
* {@inheritDoc}
*/
public final Collection<ResourcePostProcessor> getPostProcessors() {
return configurablePostProcessors.getConfiguredStrategies();
}
/**
* @param map
* containing preProcessors with corresponding alias (as key). The map must not be null and once set, the
* default map will be overridden.
*/
public ConfigurableProcessorsFactory setPreProcessorsMap(final Map<String, ResourcePreProcessor> map) {
Validate.notNull(map);
this.preProcessorsMap = map;
return this;
}
/**
* @param map
* containing postProcessors with corresponding alias (as key). The map must not be null and once set, the
* default map will be overridden.
*/
public ConfigurableProcessorsFactory setPostProcessorsMap(final Map<String, ResourcePostProcessor> map) {
Validate.notNull(map);
this.postProcessorsMap = map;
return this;
}
public ConfigurableProcessorsFactory setProperties(final Properties properties) {
Validate.notNull(properties);
this.properties = properties;
return this;
}
/**
* By default the processor will be discovered using {@link ServiceRegistry} pattern (by inspecting META-INF/services
* folder of each dependency).
*
* @return a default map of preProcessors.
*/
protected Map<String, ResourcePreProcessor> newPreProcessorsMap() {
return new HashMap<String, ResourcePreProcessor>();
}
/**
* By default the processor will be discovered using {@link ServiceRegistry} pattern (by inspecting META-INF/services
* folder of each dependency).
*
* @return a default map of postProcessors.
*/
protected Map<String, ResourcePostProcessor> newPostProcessorsMap() {
return new HashMap<String, ResourcePostProcessor>();
}
/**
* To be used for internal usage. Ensure that returned object is not null.
*/
private Properties getProperties() {
if (this.properties == null) {
this.properties = newProperties();
}
return this.properties;
}
/**
* To be used for internal usage. Ensure that returned object is not null.
*/
private Map<String, ResourcePreProcessor> getPreProcessorsMap() {
if (this.preProcessorsMap == null) {
synchronized (this) {
if (this.preProcessorsMap == null) {
this.preProcessorsMap = newPreProcessorsMap();
}
}
}
return this.preProcessorsMap;
}
/**
* To be used for internal usage. Ensure that returned object is not null.
*/
private Map<String, ResourcePostProcessor> getPostProcessorsMap() {
if (this.postProcessorsMap == null) {
synchronized (this) {
if (this.postProcessorsMap == null) {
this.postProcessorsMap = newPostProcessorsMap();
}
}
}
return this.postProcessorsMap;
}
/**
* @VisibleForTesting
* @return the list of all available pre processors;
*/
Collection<ResourcePreProcessor> getAvailablePreProcessors() {
return configurablePreProcessors.getAvailableStrategies();
}
/**
* @VisibleForTesting
* @return the list of all available pre processors;
*/
Collection<ResourcePostProcessor> getAvailablePostProcessors() {
return configurablePostProcessors.getAvailableStrategies();
}
protected Map<String, ResourcePostProcessor> getPostProcessorStrategies(final ProcessorProvider provider) {
return provider.providePostProcessors();
}
protected Map<String, ResourcePreProcessor> getPreProcessorStrategies(final ProcessorProvider provider) {
return provider.providePreProcessors();
}
}