class JRubyRunner extends AbstractLocalFileScriptRunner {

  public static final String NAME = "JRuby";
  public static final String TYPE = "text/ruby";
  public static final String[] EXTENSIONS = new String[]{"rb"};

  //<editor-fold desc="00 initialization">
  @Override
  public boolean isAbortSupported() {
    return true;
  }

  @Override
  public boolean isSupported() {
    return jrubySupport.isSupported();
  }

  @Override
  public String getName() {
    return NAME;
  }

  @Override
  public String[] getExtensions() {
    return EXTENSIONS.clone();
  }

  @Override
  public String getType() {
    return TYPE;
  }

  @Override
  public void doClose() {
    jrubySupport.interpreterTerminate();
    redirected = false;
  }

  static JRubySupport jrubySupport = null;

  private int lvl = 3;

  @Override
  protected void doInit(String[] args) {
    // Since we have a static interpreter, we have to synchronize class wide
    synchronized (JRubyRunner.class) {
      if (null == jrubySupport) {
        jrubySupport = JRubySupport.get();
        // execute script headers to already do the warmup during init
        jrubySupport.executeScriptHeader(codeBefore);
      }
    }
  }
  //</editor-fold>

  private String injectAbortWatcher(String script) {
    return "Thread.new(){\n"
         + "  runner = org.sikuli.support.ide.Runner.getRunner(\"" + NAME + "\")\n"
         + "  while runner.isRunning()\n"
         + "    sleep(0.1)\n"
         + "    if runner.isAborted()\n"
         + "      exit!\n"
         + "    end\n"
         + "  end\n"
         + "}\n" + script;
  }

  private int injectAbortWatcherLineCount() {
    return injectAbortWatcher("").split("\n").length;
  }

  //<editor-fold desc="10 run scripts">
  @Override
  protected int doRunScript(String scriptFile, String[] scriptArgs, IRunner.Options options) {<FILL_FUNCTION_BODY>}

  @Override
  protected void doRunLines(String lines, IRunner.Options options) {
    // Since we have a static interpreter, we have to synchronize class wide
    synchronized (JRubyRunner.class) {
      try {
        lines = injectAbortWatcher(lines);
        jrubySupport.interpreterRunScriptletString(lines);
      } catch (Exception ex) {
      }
    }
  }

  @Override
  protected int doEvalScript(String script, IRunner.Options options) {
    // Since we have a static interpreter, we have to synchronize class wide
    synchronized (JRubyRunner.class) {
      script = injectAbortWatcher(script);
      jrubySupport.executeScriptHeader(codeBefore);
      jrubySupport.interpreterRunScriptletString(script);
      return 0;
    }
  }
  //</editor-fold>

  //<editor-fold desc="20 redirect">
  private static boolean redirected = false;

  @Override
  protected boolean doRedirect(PrintStream stdout, PrintStream stderr) {
    //TODO extra redirect for interpreter needed? for what?
    // Since we have a static interpreter, we have to synchronize class wide
//    synchronized (JRubyRunner.class) {
//      if (!redirected) {
//        redirected = true;
//        return jrubySupport.interpreterRedirect(stdout, stderr);
//      }
//      return true;
//    }
      return true;
  }

  @Override
  protected void doAbort() {
    // Do nothing
  }
  //</editor-fold>
}

    // Since we have a static interpreter, we have to synchronize class wide
    Integer exitCode = 0;
    Object exitValue = null;
    synchronized (JRubyRunner.class) {
      File rubyFile = new File(new File(scriptFile).getAbsolutePath());
      jrubySupport.fillSysArgv(rubyFile, scriptArgs);

      jrubySupport.executeScriptHeader(codeBefore,
              rubyFile.getParentFile().getAbsolutePath(),
              rubyFile.getParentFile().getParentFile().getAbsolutePath());

      String script;
      try {
        script = FileUtils.readFileToString(rubyFile, "UTF-8");
      } catch (IOException ex) {
        log(-1, "reading script: %s", ex.getMessage());
        return Runner.FILE_NOT_FOUND;
      }

      script = injectAbortWatcher(script);

      try {
        //TODO handle exitValue (the result of the last line in the script or the value returned by statement return something)
        exitValue = jrubySupport.interpreterRunScriptletString(script);
      } catch (Throwable scriptException) {
        if(!isAborted()) {
          java.util.regex.Pattern p = java.util.regex.Pattern.compile("SystemExit: ([0-9]+)");
          Matcher matcher = p.matcher(scriptException.toString());
          if (matcher.find()) {
            exitCode = Integer.parseInt(matcher.group(1));
            Debug.info("Exit code: " + exitCode);
          } else {
            //TODO to be optimized (avoid double message)
            int errorExit = jrubySupport.findErrorSource(scriptException, rubyFile.getAbsolutePath(), injectAbortWatcherLineCount());
            options.setErrorLine(errorExit);
          }
        }
      }
      return exitCode;
    }

  
<methods>public non-sealed void <init>() ,public boolean canHandle(java.lang.String) ,public void checkAndSetConsole() <variables>private static final Deque<java.lang.String> PREVIOUS_BUNDLE_PATHS