content: package ro.isdc.wro.extensions.processor.support.typescript;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.SequenceInputStream;
  
  import org.mozilla.javascript.NativeArray;
  import org.mozilla.javascript.NativeObject;
  import org.mozilla.javascript.ScriptableObject;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import ro.isdc.wro.WroRuntimeException;
  import ro.isdc.wro.extensions.script.RhinoScriptBuilder;
  import ro.isdc.wro.util.StopWatch;
  import ro.isdc.wro.util.WroUtil;
  
  
  /**
   * Uses rhino to compile a TypeScript into javascript.
   *
   * @author Alex Objelean
   * @since 1.6.3
   * @created 18 Jan 2013
   */
  public class TypeScriptCompiler {
    private static final String PARAM_ERRORS = "errors";
    private static final String PARAM_SOURCE = "source";
    private static final Logger LOG = LoggerFactory.getLogger(TypeScriptCompiler.class);
    private static final String TYPESCRIPT_JS = "typescript-0.8.js";
    private static final String TYPESCRIPT_COMPILE_JS = "typescript.compile-0.3.js";
    private final String ecmaScriptVersion = "TypeScript.CodeGenTarget.ES5";
    private ScriptableObject scope;
  
    public String compile(final String typeScript) {
      final StopWatch stopWatch = new StopWatch();
      stopWatch.start("initContext");
      final RhinoScriptBuilder builder = initScriptBuilder();
      stopWatch.stop();
  
      stopWatch.start("compile");
      try {
        final String execute = getCompilationCommand(typeScript);
        final NativeObject compilationResult = (NativeObject) builder.evaluate(execute, "compile");
        final NativeArray errors = (NativeArray) compilationResult.get(PARAM_ERRORS);
        if (errors.size() > 0) {
          throwCompilationError(errors);
        }
        return compilationResult.get(PARAM_SOURCE).toString();
      } finally {
        stopWatch.stop();
        LOG.debug(stopWatch.prettyPrint());
      }
    }
  
    private void throwCompilationError(final NativeArray errors) {
      final StringBuilder sb = new StringBuilder();
      for (final Object error : errors) {
        sb.append(error.toString()).append("\n");
      }
      LOG.debug("Compilation errors: {}", sb);
      throw new WroRuntimeException(sb.toString());
    }
  
    /**
     * Creates compilation command for provided typescript input.
     *
     * @param input
     *          the TypeScript to compile into js.
     * @return compilation command.
     */
    private String getCompilationCommand(final String input) {
      return String.format("compilerWrapper.compile(%s, %s)", WroUtil.toJSMultiLineString(input),
          ecmaScriptVersion);
    }
  
    /**
     * @return the stream of the compiler resource (javascript) used to compile templates.
     * @throws IOException
     *           if the stream could not be located.
     */
    private InputStream getCompilerAsStream()
        throws IOException {
      return new SequenceInputStream(getCompilerStream(), getCompilerWrapperStream());
    }
  
    /**
     * @return the stream for the script performing actual compilation wrapper.
     */
    protected InputStream getCompilerWrapperStream() {
      return TypeScriptCompiler.class.getResourceAsStream(TYPESCRIPT_COMPILE_JS);
    }
  
    /**
     * @return the stream for the script for TypeScript compiler.
     */
    protected InputStream getCompilerStream() {
      return TypeScriptCompiler.class.getResourceAsStream(TYPESCRIPT_JS);
    }
  
    private RhinoScriptBuilder initScriptBuilder() {
      try {
        RhinoScriptBuilder builder;
        if (scope == null) {
          builder = RhinoScriptBuilder.newClientSideAwareChain().evaluateChain(getCompilerAsStream(), "templateCompiler.js");
          scope = builder.getScope();
        } else {
          builder = RhinoScriptBuilder.newChain(scope);
        }
        return builder;
      } catch (final IOException ex) {
        throw new WroRuntimeException("Failed reading init script", ex);
      }
    }
  }
  