content: /*
   * 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();
    }
  }
  