content: /*
   * Copyright wro4j@2011.
   */
  package ro.isdc.wro.extensions.processor.support.csslint;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.lang.reflect.Type;
  import java.util.List;
  
  import org.mozilla.javascript.ScriptableObject;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import ro.isdc.wro.extensions.processor.support.linter.OptionsBuilder;
  import ro.isdc.wro.extensions.script.RhinoScriptBuilder;
  import ro.isdc.wro.util.StopWatch;
  import ro.isdc.wro.util.WroUtil;
  
  import com.google.gson.Gson;
  import com.google.gson.reflect.TypeToken;
  
  
  /**
   * CssLint script engine utility. The underlying implementation uses CSSLint script utility<br/>
   * {@link https ://github.com/stubbornella/csslint}. The underlying csslint version is 0.9.10.
   *
   * @author Alex Objelean
   * @since 1.3.8
   * @created 19 Jun 2011
   */
  public class CssLint {
    private static final Logger LOG = LoggerFactory.getLogger(CssLint.class);
    /**
     * The name of the csslint script to be used by default.
     */
    private static final String DEFAULT_CSSLINT_JS = "csslint.min.js";
    private final OptionsBuilder optionsBuilder = new OptionsBuilder();
    /**
     * CSV Options to apply.
     */
    private String options;
    private ScriptableObject scope;
  
    /**
     * Initialize script builder for evaluation.
     */
    private RhinoScriptBuilder initScriptBuilder() {
      try {
        RhinoScriptBuilder builder = null;
        if (scope == null) {
          builder = RhinoScriptBuilder.newChain().evaluateChain(getScriptAsStream(), DEFAULT_CSSLINT_JS);
          scope = builder.getScope();
        } else {
          builder = RhinoScriptBuilder.newChain(scope);
        }
        return builder;
      } catch (final IOException ex) {
        throw new IllegalStateException("Failed reading init script", ex);
      }
    }
  
    /**
     * @return the stream of the csslint script. Override this method to provide a different script version.
     */
    protected InputStream getScriptAsStream() {
      return CssLint.class.getResourceAsStream(DEFAULT_CSSLINT_JS);
    }
  
    /**
     * Validates a js using jsHint and throws {@link CssLintException} if the js is invalid. If no exception is thrown,
     * the js is valid.
     *
     * @param data
     *          js content to process.
     * @throws CssLintException
     *           when parsed css has some kind of problems.
     */
    public void validate(final String data)
        throws CssLintException {
      final StopWatch watch = new StopWatch();
      watch.start("init");
      final RhinoScriptBuilder builder = initScriptBuilder();
      watch.stop();
      watch.start("cssLint");
      LOG.debug("options: {}", this.options);
      final String script = buildCssLintScript(WroUtil.toJSMultiLineString(data));
      LOG.debug("script: {}", script);
      builder.evaluate(script, "CSSLint.verify").toString();
      final boolean valid = Boolean.parseBoolean(builder.evaluate("result.length == 0", "checkNoErrors").toString());
      if (!valid) {
        final String json = builder.addJSON().evaluate("JSON.stringify(result)", "CssLint messages").toString();
        LOG.debug("json {}", json);
        final Type type = new TypeToken<List<CssLintError>>() {}.getType();
        final List<CssLintError> errors = new Gson().fromJson(json, type);
        LOG.debug("Errors: {}", errors);
        throw new CssLintException().setErrors(errors);
      }
      LOG.debug("isValid: {}", valid);
      watch.stop();
      LOG.debug(watch.prettyPrint());
    }
  
    private String buildCssLintScript(final String data) {
      return String.format("var result = CSSLint.verify(%s,%s).messages", data, optionsBuilder.buildFromCsv(options));
    }
  
    /**
     * @param options CSV representation of the options.
     */
    public CssLint setOptions(final String options) {
      this.options = options;
      return this;
    }
  
  //
  //  /**
  //   * @param options
  //   *          the options to set
  //   */
  //  public CssLint setOptions(final String... options) {
  //    if (options != null && options.length > 0) {
  //      this.options = options.length > 1 ? options : optionsBuilder.splitOptions(options[0]);
  //    } else {
  //      this.options = ArrayUtils.EMPTY_STRING_ARRAY;
  //    }
  //    LOG.debug("options: {}", Arrays.asList(this.options));
  //    return this;
  //  }
  }
  