content: /*
   * Copyright 2010.
   */
  package ro.isdc.wro.extensions.processor.support.coffeescript;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.Arrays;
  
  import org.mozilla.javascript.ScriptableObject;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import ro.isdc.wro.extensions.locator.WebjarUriLocator;
  import ro.isdc.wro.extensions.processor.support.linter.LinterException;
  import ro.isdc.wro.extensions.script.RhinoScriptBuilder;
  import ro.isdc.wro.util.WroUtil;
  
  
  /**
   * CoffeeScript is a little language that compiles into JavaScript. Underneath all of those embarrassing braces and
   * semicolons, JavaScript has always had a gorgeous object model at its heart. CoffeeScript is an attempt to expose the
   * good parts of JavaScript in a simple way.
   * <p/>
   * This processor loads coffee-script library from the webjar.
   *
   * @author Alex Objelean
   * @since 1.3.6
   */
  public class CoffeeScript {
    private static final Logger LOG = LoggerFactory.getLogger(CoffeeScript.class);
    private static final String DEFAULT_COFFE_SCRIPT = "coffee-script.js";
    private String[] options;
    private ScriptableObject scope;
    private WebjarUriLocator webjarLocator;
  
    /**
     * Initialize script builder for evaluation.
     */
    private RhinoScriptBuilder initScriptBuilder() {
      try {
        RhinoScriptBuilder builder = null;
        if (scope == null) {
          builder = RhinoScriptBuilder.newChain().evaluateChain(getCoffeeScriptAsStream(), DEFAULT_COFFE_SCRIPT);
          scope = builder.getScope();
        } else {
          builder = RhinoScriptBuilder.newChain(scope);
        }
        return builder;
      } catch (final IOException ex) {
        throw new IllegalStateException("Failed reading init script", ex);
      }
    }
  
  
    /**
     * Override this method to use a different version of CoffeeScript. This method is useful for upgrading coffeeScript
     * processor independently of wro4j.
     *
     * @return The stream of the CoffeeScript.
     */
    protected InputStream getCoffeeScriptAsStream() throws IOException {
      return getWebjarLocator().locate(WebjarUriLocator.createUri("coffee-script.min.js"));
    }
  
    /**
     * @return {@link WebjarUriLocator} instance to retrieve webjars.
     */
    private WebjarUriLocator getWebjarLocator() {
      if (webjarLocator == null) {
        webjarLocator = new WebjarUriLocator();
      }
      return webjarLocator;
    }
  
    /**
     * Validates a js using jsHint and throws {@link LinterException} if the js is invalid. If no exception is thrown, the
     * js is valid.
     *
     * @param data
     *          js content to process.
     */
    public String compile(final String data) {
      final RhinoScriptBuilder builder = initScriptBuilder();
      final String compileScript = String.format("CoffeeScript.compile(%s, %s);", WroUtil.toJSMultiLineString(data),
          buildOptions());
      return (String) builder.evaluate(compileScript, "CoffeeScript.compile");
    }
  
    /**
     * @return A javascript representation of the options. The result is a json object.
     */
    private String buildOptions() {
      final StringBuffer sb = new StringBuffer("{");
      if (options != null) {
        for (int i = 0; i < options.length; i++) {
          sb.append(options[i]).append(": true");
          if (i < options.length - 1) {
            sb.append(",");
          }
        }
      }
      sb.append("}");
      return sb.toString();
    }
  
    /**
     * @param options
     *          the options to set
     */
    public CoffeeScript setOptions(final String... options) {
      this.options = options == null ? new String[] {} : options;
      LOG.debug("setOptions: {}", Arrays.asList(this.options));
      return this;
    }
  }
  