/*
* =============================================================================
*
* Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org)
*
* 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.thymeleaf;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.thymeleaf.cache.ICacheManager;
import org.thymeleaf.dialect.IDialect;
import org.thymeleaf.dialect.IExecutionAttributeDialect;
import org.thymeleaf.dialect.IExpressionObjectDialect;
import org.thymeleaf.dialect.IPostProcessorDialect;
import org.thymeleaf.dialect.IPreProcessorDialect;
import org.thymeleaf.dialect.IProcessorDialect;
import org.thymeleaf.expression.IExpressionObjectFactory;
import org.thymeleaf.linkbuilder.ILinkBuilder;
import org.thymeleaf.messageresolver.IMessageResolver;
import org.thymeleaf.postprocessor.IPostProcessor;
import org.thymeleaf.preprocessor.IPreProcessor;
import org.thymeleaf.processor.IProcessor;
import org.thymeleaf.processor.cdatasection.ICDATASectionProcessor;
import org.thymeleaf.processor.comment.ICommentProcessor;
import org.thymeleaf.processor.doctype.IDocTypeProcessor;
import org.thymeleaf.processor.element.IElementModelProcessor;
import org.thymeleaf.processor.element.IElementTagProcessor;
import org.thymeleaf.processor.element.MatchingAttributeName;
import org.thymeleaf.processor.element.MatchingElementName;
import org.thymeleaf.processor.processinginstruction.IProcessingInstructionProcessor;
import org.thymeleaf.processor.text.ITextProcessor;
import org.thymeleaf.processor.xmldeclaration.IXMLDeclarationProcessor;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;
import org.thymeleaf.util.ProcessorComparators;
import org.thymeleaf.util.StringUtils;
/**
*
* @author Daniel Fernández
*
* @since 1.0
*
*/
final class ConfigurationPrinterHelper {
public static final String CONFIGURATION_LOGGER_NAME = org.thymeleaf.TemplateEngine.class.getName() + ".CONFIG";
private static final Logger configLogger = LoggerFactory.getLogger(CONFIGURATION_LOGGER_NAME);
static void printConfiguration(final IEngineConfiguration configuration) {
final ConfigLogBuilder logBuilder = new ConfigLogBuilder();
final ICacheManager cacheManager = configuration.getCacheManager();
final Set<ITemplateResolver> templateResolvers = configuration.getTemplateResolvers();
final Set<IMessageResolver> messageResolvers = configuration.getMessageResolvers();
final Set<ILinkBuilder> linkBuilders = configuration.getLinkBuilders();
logBuilder.line("Initializing Thymeleaf Template engine configuration...");
logBuilder.line("[THYMELEAF] TEMPLATE ENGINE CONFIGURATION:");
if (!StringUtils.isEmptyOrWhitespace(Thymeleaf.VERSION)) {
if (!StringUtils.isEmptyOrWhitespace(Thymeleaf.BUILD_TIMESTAMP)) {
logBuilder.line("[THYMELEAF] * Thymeleaf version: {} (built {})", Thymeleaf.VERSION, Thymeleaf.BUILD_TIMESTAMP);
} else {
logBuilder.line("[THYMELEAF] * Thymeleaf version: {}", Thymeleaf.VERSION);
}
}
logBuilder.line("[THYMELEAF] * Cache Manager implementation: {}", (cacheManager == null? "[no caches]" : cacheManager.getClass().getName()));
logBuilder.line("[THYMELEAF] * Template resolvers:");
for (final ITemplateResolver templateResolver : templateResolvers) {
if (templateResolver.getOrder() != null) {
logBuilder.line("[THYMELEAF] * [{}] {}", templateResolver.getOrder(), templateResolver.getName());
} else{
logBuilder.line("[THYMELEAF] * {}", templateResolver.getName());
}
}
logBuilder.line("[THYMELEAF] * Message resolvers:");
for (final IMessageResolver messageResolver : messageResolvers) {
if (messageResolver.getOrder() != null) {
logBuilder.line("[THYMELEAF] * [{}] {}", messageResolver.getOrder(), messageResolver.getName());
} else{
logBuilder.line("[THYMELEAF] * {}", messageResolver.getName());
}
}
logBuilder.line("[THYMELEAF] * Link builders:");
for (final ILinkBuilder linkBuilder : linkBuilders) {
if (linkBuilder.getOrder() != null) {
logBuilder.line("[THYMELEAF] * [{}] {}", linkBuilder.getOrder(), linkBuilder.getName());
} else{
logBuilder.line("[THYMELEAF] * {}", linkBuilder.getName());
}
}
final Set<DialectConfiguration> dialectConfigurations = configuration.getDialectConfigurations();
int dialectIndex = 1;
final Integer totalDialects = Integer.valueOf(dialectConfigurations.size());
for (final DialectConfiguration dialectConfiguration : dialectConfigurations) {
final IDialect dialect = dialectConfiguration.getDialect();
if (totalDialects.intValue() > 1) {
logBuilder.line("[THYMELEAF] * Dialect [{} of {}]: {} ({})", new Object[]{Integer.valueOf(dialectIndex), totalDialects, dialect.getName(), dialect.getClass().getName()});
} else {
logBuilder.line("[THYMELEAF] * Dialect: {} ({})", dialect.getName(), dialect.getClass().getName());
}
String dialectPrefix = null;
if (dialect instanceof IProcessorDialect) {
dialectPrefix = (dialectConfiguration.isPrefixSpecified()? dialectConfiguration.getPrefix() : ((IProcessorDialect) dialect).getPrefix());
logBuilder.line("[THYMELEAF] * Prefix: \"{}\"", (dialectPrefix != null ? dialectPrefix : "(none)"));
}
if (configLogger.isDebugEnabled()) {
printDebugConfiguration(logBuilder, dialect, dialectPrefix);
}
dialectIndex++;
}
logBuilder.end("[THYMELEAF] TEMPLATE ENGINE CONFIGURED OK");
/*
* The following condition makes sense because contents in each case will differ a lot.
*/
if (configLogger.isTraceEnabled()) {
configLogger.trace(logBuilder.toString());
} else if (configLogger.isDebugEnabled()) {
configLogger.debug(logBuilder.toString());
}
}
private static void printDebugConfiguration(final ConfigLogBuilder logBuilder, final IDialect idialect, final String dialectPrefix) {
if (idialect instanceof IProcessorDialect) {
final IProcessorDialect dialect = (IProcessorDialect)idialect;
final Set<IProcessor> processors = dialect.getProcessors(dialectPrefix);
printProcessorsForTemplateMode(logBuilder, processors, TemplateMode.HTML);
printProcessorsForTemplateMode(logBuilder, processors, TemplateMode.XML);
printProcessorsForTemplateMode(logBuilder, processors, TemplateMode.TEXT);
printProcessorsForTemplateMode(logBuilder, processors, TemplateMode.JAVASCRIPT);
printProcessorsForTemplateMode(logBuilder, processors, TemplateMode.CSS);
printProcessorsForTemplateMode(logBuilder, processors, TemplateMode.RAW);
}
if (idialect instanceof IPreProcessorDialect) {
final IPreProcessorDialect dialect = (IPreProcessorDialect)idialect;
final Set<IPreProcessor> preProcessors = dialect.getPreProcessors();
printPreProcessorsForTemplateMode(logBuilder, preProcessors, TemplateMode.HTML);
printPreProcessorsForTemplateMode(logBuilder, preProcessors, TemplateMode.XML);
printPreProcessorsForTemplateMode(logBuilder, preProcessors, TemplateMode.TEXT);
printPreProcessorsForTemplateMode(logBuilder, preProcessors, TemplateMode.JAVASCRIPT);
printPreProcessorsForTemplateMode(logBuilder, preProcessors, TemplateMode.CSS);
printPreProcessorsForTemplateMode(logBuilder, preProcessors, TemplateMode.RAW);
}
if (idialect instanceof IPostProcessorDialect) {
final IPostProcessorDialect dialect = (IPostProcessorDialect)idialect;
final Set<IPostProcessor> postProcessors = dialect.getPostProcessors();
printPostProcessorsForTemplateMode(logBuilder, postProcessors, TemplateMode.HTML);
printPostProcessorsForTemplateMode(logBuilder, postProcessors, TemplateMode.XML);
printPostProcessorsForTemplateMode(logBuilder, postProcessors, TemplateMode.TEXT);
printPostProcessorsForTemplateMode(logBuilder, postProcessors, TemplateMode.JAVASCRIPT);
printPostProcessorsForTemplateMode(logBuilder, postProcessors, TemplateMode.CSS);
printPostProcessorsForTemplateMode(logBuilder, postProcessors, TemplateMode.RAW);
}
if (idialect instanceof IExpressionObjectDialect) {
final IExpressionObjectDialect dialect = (IExpressionObjectDialect)idialect;
final IExpressionObjectFactory expressionObjectFactory = dialect.getExpressionObjectFactory();
if (expressionObjectFactory != null) {
final Set<String> expressionObjectNames = expressionObjectFactory.getAllExpressionObjectNames();
if (expressionObjectNames != null && !expressionObjectNames.isEmpty()) {
logBuilder.line("[THYMELEAF] * Expression Objects:");
for (final String expressionObjectName : expressionObjectNames) {
logBuilder.line("[THYMELEAF] * #{}", new Object[] {expressionObjectName});
}
}
}
}
if (idialect instanceof IExecutionAttributeDialect) {
final IExecutionAttributeDialect dialect = (IExecutionAttributeDialect)idialect;
final Map<String, Object> executionAttributes = dialect.getExecutionAttributes();
if (executionAttributes != null && !executionAttributes.isEmpty()) {
logBuilder.line("[THYMELEAF] * Execution Attributes:");
for (final Map.Entry<String,Object> executionAttributesEntry : executionAttributes.entrySet()) {
final String attrName = executionAttributesEntry.getKey();
final String attrValue =
(executionAttributesEntry.getValue() == null? null : executionAttributesEntry.getValue().toString());
logBuilder.line("[THYMELEAF] * \"{}\": {}", new Object[] {attrName, attrValue});
}
}
}
}
private static void printProcessorsForTemplateMode(final ConfigLogBuilder logBuilder, final Set<IProcessor> processors, final TemplateMode templateMode) {
if (processors == null || processors.isEmpty()) {
return;
}
final List<ICDATASectionProcessor> cdataSectionProcessors = new ArrayList<ICDATASectionProcessor>();
final List<ICommentProcessor> commentProcessors = new ArrayList<ICommentProcessor>();
final List<IDocTypeProcessor> docTypeProcessors = new ArrayList<IDocTypeProcessor>();
final List<IElementTagProcessor> elementTagProcessors = new ArrayList<IElementTagProcessor>();
final List<IElementModelProcessor> elementModelProcessors = new ArrayList<IElementModelProcessor>();
final List<IProcessingInstructionProcessor> processingInstructionProcessors = new ArrayList<IProcessingInstructionProcessor>();
final List<ITextProcessor> textProcessors = new ArrayList<ITextProcessor>();
final List<IXMLDeclarationProcessor> xmlDeclarationProcessors = new ArrayList<IXMLDeclarationProcessor>();
boolean processorsForTemplateModeExist = false;
for (final IProcessor processor : processors) {
if (!templateMode.equals(processor.getTemplateMode())) {
continue;
}
processorsForTemplateModeExist = true;
if (processor instanceof ICDATASectionProcessor) {
cdataSectionProcessors.add((ICDATASectionProcessor) processor);
} else if (processor instanceof ICommentProcessor) {
commentProcessors.add((ICommentProcessor) processor);
} else if (processor instanceof IDocTypeProcessor) {
docTypeProcessors.add((IDocTypeProcessor) processor);
} else if (processor instanceof IElementTagProcessor) {
elementTagProcessors.add((IElementTagProcessor) processor);
} else if (processor instanceof IElementModelProcessor) {
elementModelProcessors.add((IElementModelProcessor) processor);
} else if (processor instanceof IProcessingInstructionProcessor) {
processingInstructionProcessors.add((IProcessingInstructionProcessor) processor);
} else if (processor instanceof ITextProcessor) {
textProcessors.add((ITextProcessor) processor);
} else if (processor instanceof IXMLDeclarationProcessor) {
xmlDeclarationProcessors.add((IXMLDeclarationProcessor) processor);
}
}
if (!processorsForTemplateModeExist) {
// Nothing to show, there are no processors for this template mode
return;
}
logBuilder.line("[THYMELEAF] * Processors for Template Mode: {}", templateMode);
Collections.sort(cdataSectionProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(commentProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(docTypeProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(elementTagProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(elementModelProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(processingInstructionProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(textProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(xmlDeclarationProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
if (!elementTagProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * Element Tag Processors by [matching element and attribute name] [precedence]:");
for (final IElementTagProcessor processor : elementTagProcessors) {
final MatchingElementName matchingElementName = processor.getMatchingElementName();
final MatchingAttributeName matchingAttributeName = processor.getMatchingAttributeName();
final String elementName = (matchingElementName == null? "*" : matchingElementName.toString());
final String attributeName = (matchingAttributeName == null? "*" : matchingAttributeName.toString());
logBuilder.line("[THYMELEAF] * [{} {}] [{}]: {}",
new Object[] {elementName, attributeName, Integer.valueOf(processor.getPrecedence()), processor.getClass().getName()});
}
}
if (!elementModelProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * Element Model Processors by [matching element and attribute name] [precedence]:");
for (final IElementModelProcessor processor : elementModelProcessors) {
final MatchingElementName matchingElementName = processor.getMatchingElementName();
final MatchingAttributeName matchingAttributeName = processor.getMatchingAttributeName();
final String elementName = (matchingElementName == null? "*" : matchingElementName.toString());
final String attributeName = (matchingAttributeName == null? "*" : matchingAttributeName.toString());
logBuilder.line("[THYMELEAF] * [{} {}] [{}]: {}",
new Object[] {elementName, attributeName, Integer.valueOf(processor.getPrecedence()), processor.getClass().getName()});
}
}
if (!textProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * Text Processors by [precedence]:");
for (final ITextProcessor processor : textProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}",
new Object[] {Integer.valueOf(processor.getPrecedence()), processor.getClass().getName()});
}
}
if (!docTypeProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * DOCTYPE Processors by [precedence]:");
for (final IDocTypeProcessor processor : docTypeProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}",
new Object[] {Integer.valueOf(processor.getPrecedence()), processor.getClass().getName()});
}
}
if (!cdataSectionProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * CDATA Section Processors by [precedence]:");
for (final ICDATASectionProcessor processor : cdataSectionProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}",
new Object[] {Integer.valueOf(processor.getPrecedence()), processor.getClass().getName()});
}
}
if (!commentProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * Comment Processors by [precedence]:");
for (final ICommentProcessor processor : commentProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}",
new Object[] {Integer.valueOf(processor.getPrecedence()), processor.getClass().getName()});
}
}
if (!xmlDeclarationProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * XML Declaration Processors by [precedence]:");
for (final IXMLDeclarationProcessor processor : xmlDeclarationProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}",
new Object[] {Integer.valueOf(processor.getPrecedence()), processor.getClass().getName()});
}
}
if (!processingInstructionProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * Processing Instruction Processors by [precedence]:");
for (final IProcessingInstructionProcessor processor : processingInstructionProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}",
new Object[] {Integer.valueOf(processor.getPrecedence()), processor.getClass().getName()});
}
}
}
private static void printPreProcessorsForTemplateMode(final ConfigLogBuilder logBuilder, final Set<IPreProcessor> preProcessors, final TemplateMode templateMode) {
if (preProcessors == null || preProcessors.isEmpty()) {
return;
}
final List<IPreProcessor> preProcessorsForTemplateMode = new ArrayList<IPreProcessor>();
for (final IPreProcessor preProcessor : preProcessors) {
if (!templateMode.equals(preProcessor.getTemplateMode())) {
continue;
}
preProcessorsForTemplateMode.add(preProcessor);
}
if (preProcessorsForTemplateMode.isEmpty()) {
// Nothing to show, there are no artifacts for this template mode
return;
}
Collections.sort(preProcessorsForTemplateMode, ProcessorComparators.PRE_PROCESSOR_COMPARATOR);
logBuilder.line("[THYMELEAF] * Pre-Processors for Template Mode: {} by [precedence]", templateMode);
for (final IPreProcessor preProcessor : preProcessorsForTemplateMode) {
logBuilder.line("[THYMELEAF] * [{}]: {}",
new Object[] {Integer.valueOf(preProcessor.getPrecedence()), preProcessor.getClass().getName()});
}
}
private static void printPostProcessorsForTemplateMode(final ConfigLogBuilder logBuilder, final Set<IPostProcessor> postProcessors, final TemplateMode templateMode) {
if (postProcessors == null || postProcessors.isEmpty()) {
return;
}
final List<IPostProcessor> postProcessorsForTemplateMode = new ArrayList<IPostProcessor>();
for (final IPostProcessor postProcessor : postProcessors) {
if (!templateMode.equals(postProcessor.getTemplateMode())) {
continue;
}
postProcessorsForTemplateMode.add(postProcessor);
}
if (postProcessorsForTemplateMode.isEmpty()) {
// Nothing to show, there are no artifacts for this template mode
return;
}
Collections.sort(postProcessorsForTemplateMode, ProcessorComparators.POST_PROCESSOR_COMPARATOR);
logBuilder.line("[THYMELEAF] * Post-Processors for Template Mode: {} by [precedence]", templateMode);
for (final IPostProcessor postProcessor : postProcessorsForTemplateMode) {
logBuilder.line("[THYMELEAF] * [{}]: {}",
new Object[]{Integer.valueOf(postProcessor.getPrecedence()), postProcessor.getClass().getName()});
}
}
private static final class ConfigLogBuilder {
private static final String PLACEHOLDER = "\\{\\}";
private final StringBuilder strBuilder;
protected ConfigLogBuilder() {
super();
this.strBuilder = new StringBuilder();
}
protected void end(final String line) {
this.strBuilder.append(line);
}
protected void line(final String line) {
this.strBuilder.append(line).append("\n");
}
protected void line(final String line, final Object p1) {
this.strBuilder.append(replace(line, p1)).append("\n");
}
protected void line(final String line, final Object p1, final Object p2) {
this.strBuilder.append(replace(replace(line, p1), p2)).append("\n");
}
protected void line(final String line, final Object[] pArr) {
String newLine = line;
for (final Object aPArr : pArr) {
newLine = replace(newLine, aPArr);
}
this.strBuilder.append(newLine).append("\n");
}
@Override
public String toString() {
return this.strBuilder.toString();
}
private String replace(final String str, final Object replacement) {
return str.replaceFirst(PLACEHOLDER, (replacement == null? "" : param(replacement)));
}
private String param(final Object p) {
if (p == null) {
return null;
}
return p.toString().replaceAll("\\$", "\\.");
}
}
private ConfigurationPrinterHelper() {
super();
}
}