/*
* Copyright (c) 2009.
*/
package ro.isdc.wro.model.resource.processor.impl.css;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.isdc.wro.model.resource.Resource;
import ro.isdc.wro.model.resource.ResourceType;
import ro.isdc.wro.model.resource.SupportedResourceType;
import ro.isdc.wro.model.resource.processor.ResourcePostProcessor;
import ro.isdc.wro.model.resource.processor.ResourcePreProcessor;
import ro.isdc.wro.util.WroUtil;
/**
* Processor of css resources, responsible for replacing variables. (@see
* http://disruptive-innovations.com/zoo/cssvariables/). This is a pre processor, because it makes sense to apply
* variables only on the same css. <br/>
* This processor is implemented as both: preprocessor & postprocessor.
*
* @author Alex Objelean
* @created Created on Jul 05, 2009
*/
@SupportedResourceType(ResourceType.CSS)
public class CssVariablesProcessor
implements ResourcePreProcessor, ResourcePostProcessor {
private static final Logger LOG = LoggerFactory.getLogger(CssVariablesProcessor.class);
public static final String ALIAS = "cssVariables";
/**
* Compiled pattern for REGEX_VARIABLES_DEFINITION regex.
*/
private static final Pattern PATTERN_VARIABLES_DEFINITION = Pattern.compile(WroUtil.loadRegexpWithKey("cssVariables.definition"));
/**
* Compiled pattern for REGEX_VARIABLES_BODY pattern.
*/
private static final Pattern PATTERN_VARIABLES_BODY = Pattern.compile(WroUtil.loadRegexpWithKey("cssVariables.body"));
/**
* Compiled pattern for REGEX_VARIABLE_HOLDER pattern.
*/
private static final Pattern PATTERN_VARIABLE_HOLDER = Pattern.compile(WroUtil.loadRegexpWithKey("cssVariables.holder"));
/**
* Extract variables map from variables body.
*
* @param variablesBody
* string containing variables mappings.
* @return map with extracted variables.
*/
private Map<String, String> extractVariables(final String variablesBody) {
final Map<String, String> map = new HashMap<String, String>();
final Matcher m = PATTERN_VARIABLES_BODY.matcher(variablesBody);
LOG.debug("parsing variables body");
while (m.find()) {
final String key = m.group(1);
final String value = m.group(2);
if (map.containsKey(key)) {
LOG.warn("A duplicate variable name found with name: {} and value: {}.", key, value);
}
map.put(key, value);
}
return map;
}
/**
* {@inheritDoc}
*/
public void process(final Reader reader, final Writer writer)
throws IOException {
process(null, reader, writer);
}
/**
* {@inheritDoc}
*/
public void process(final Resource resource, final Reader reader, final Writer writer)
throws IOException {
try {
final String css = IOUtils.toString(reader);
final String result = parseCss(css);
writer.write(result);
} finally {
reader.close();
writer.close();
}
}
/**
* Parse css, find all defined variables & replace them.
*
* @param css
* to parse.
*/
private String parseCss(final String css) {
// map containing variables & their values
final Map<String, String> map = new HashMap<String, String>();
final StringBuffer sb = new StringBuffer();
final Matcher m = PATTERN_VARIABLES_DEFINITION.matcher(css);
while (m.find()) {
final String variablesBody = m.group(1);
// LOG.debug("variables body: " + variablesBody);
// extract variables
map.putAll(extractVariables(variablesBody));
// remove variables definition
m.appendReplacement(sb, "");
}
m.appendTail(sb);
final String result = replaceVariables(sb.toString(), map);
// LOG.debug("replaced variables: " + result);
return result;
}
/**
* Replace variables from css with provided variables map.
*
* @param cleanCss
* used to replace variable placeholders.
* @param variables
* map of variables used for substitution.
* @return css with all variables replaced.
*/
private String replaceVariables(final String css, final Map<String, String> variables) {
final StringBuffer sb = new StringBuffer();
final Matcher m = PATTERN_VARIABLE_HOLDER.matcher(css);
while (m.find()) {
final String oldMatch = m.group();
final String variableName = m.group(1);
final String variableValue = variables.get(variableName);
if (variableValue != null) {
final String newReplacement = oldMatch.replace(oldMatch, variableValue);
m.appendReplacement(sb, newReplacement.trim());
} else {
LOG.warn("No variable with name " + variableName + " was found!");
}
}
m.appendTail(sb);
return sb.toString();
}
}