---
title: Rhinoでgoogle-code-prettifyを実行する
author: aterai
pubdate: 2011-01-25T23:52:05+09:00
description: Rhino(またはNashorn)でgoogle-prettify.jsを実行し、ソースコードをハイライト済みのHtmlファイルに変換します。
---
#contents

* 概要 [#summary]
`Rhino`(または`Nashorn`)で`google-prettify.js`を実行し、ソースコードをハイライト済みの`Html`ファイルに変換します。

#ref(https://lh5.googleusercontent.com/-fBSp3mL32JA/T-4FRzdTzYI/AAAAAAAABOc/xDs9vq7gqLw/s800/GooglePrettifyRhinoTest.png)

* サンプルコード [#sourcecode]
#code{{
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.script.*;
import javax.swing.*;
import javax.swing.text.html.*;

public class GooglePrettifyRhinoTest {
  private final JTextArea src = new JTextArea();
  private final JTextArea dst = new JTextArea();
  private final JEditorPane editor = new JEditorPane();
  private final ScriptEngine engine = createEngine();
  public JComponent makeUI() {
    try (Reader reader = new BufferedReader(new InputStreamReader(
        new FileInputStream("GooglePrettifyRhinoTest.java"), "UTF-8"))) {
      src.read(reader, "");
    } catch(Exception ex) {
      ex.printStackTrace();
    }

    StyleSheet styleSheet = new StyleSheet();
    styleSheet.addRule(".str {color:#008800}");
    styleSheet.addRule(".kwd {color:#000088}");
    styleSheet.addRule(".com {color:#880000}");
    styleSheet.addRule(".typ {color:#660066}");
    styleSheet.addRule(".lit {color:#006666}");
    styleSheet.addRule(".pun {color:#666600}");
    styleSheet.addRule(".pln {color:#000000}");
    styleSheet.addRule(".tag {color:#000088}");
    styleSheet.addRule(".atn {color:#660066}");
    styleSheet.addRule(".atv {color:#008800}");
    styleSheet.addRule(".dec {color:#660066}");
    HTMLEditorKit htmlEditorKit = new HTMLEditorKit();
    htmlEditorKit.setStyleSheet(styleSheet);
    editor.setEditorKit(htmlEditorKit);

    JButton b = new JButton((new AbstractAction("Convert to google sites") {
      String pre = "<pre>";
      @Override public void actionPerformed(ActionEvent e) {
        String txt = src.getText();
        txt = txt.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
        String str = prettify(engine, txt);
        editor.setText(pre + str + "\n</pre>");
        str = str.replace("class=\"str\"", "style=\"color:#080\"");
        str = str.replace("class=\"kwd\"", "style=\"color:#008\"");
        str = str.replace("class=\"com\"", "style=\"color:#800\"");
        str = str.replace("class=\"typ\"", "style=\"color:#606\"");
        str = str.replace("class=\"lit\"", "style=\"color:#066\"");
        str = str.replace("class=\"pun\"", "style=\"color:#660\"");
        str = str.replace("class=\"pln\"", "style=\"color:#000\"");
        str = str.replace("class=\"tag\"", "style=\"color:#008\"");
        str = str.replace("class=\"atn\"", "style=\"color:#606\"");
        str = str.replace("class=\"atv\"", "style=\"color:#080\"");
        str = str.replace("class=\"dec\"", "style=\"color:#606\"");
        dst.setText(pre + str + "\n</pre>");
      }
    }));

    JTabbedPane tab = new JTabbedPane();
    tab.addTab("Google sites html", new JScrollPane(dst));
    tab.addTab("JEditorPane preview", new JScrollPane(editor));
    JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
    sp.setResizeWeight(.5);
    sp.setTopComponent(new JScrollPane(src));
    sp.setBottomComponent(tab);
    JPanel p = new JPanel(new BorderLayout());
    p.add(b, BorderLayout.SOUTH);
    p.add(sp);
    return p;
  }
  public static ScriptEngine createEngine() {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
//     ScriptEngineFactory factory = engine.getFactory();
//     String name = factory.getEngineName();
//     String version = factory.getEngineVersion();
//     System.out.printf("\tScript Engine: %s (%s)\n", name, version);

    //String p = "https://raw.githubusercontent.com/google/code-prettify/master/src/prettify.js";
    //String p = "http://google-code-prettify.googlecode.com/svn-history/r120/trunk/src/prettify.js";
    String p = "https://raw.githubusercontent.com/google/code-prettify/f5ad44e3253f1bc8e288477a36b2ce5972e8e161/src/prettify.js";

    try (Reader reader = new BufferedReader(new InputStreamReader(new URL(p).openStream()))) {
      engine.eval("var window={}, navigator=null;");
      engine.eval(reader);
      return engine;
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    return null;
  }
  public static String prettify(ScriptEngine engine, String src) {
    try {
      Object w = engine.get("window");
      return (String) ((Invocable) engine).invokeMethod(w, "prettyPrintOne", src);
    } catch (Exception e) {
      e.printStackTrace();
      return "";
    }
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new GooglePrettifyRhinoTest().makeUI());
    f.setSize(640, 640);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}
}}

* 解説 [#explanation]
上記のサンプルでは、`new ScriptEngineManager().getEngineByName("JavaScript");`で取得した`JavaScript`エンジン(`Rhino`)に、`prettify.js`(`r120`) を読み込んで、`prettify.js`の`prettyPrintOne`メソッドを実行しています。

`google sites`では、`css`ファイルも使用できないので、`replace("class=\"kwd\"", "style=\"color:#008\"");`のように、クラスをスタイルの色に全部置換しています。

----
- `org.mozilla.javascript.Context`などを使用する場合

 > "%JAVA_HOME%\bin\javac" -cp .;rhino-1.7R4.jar GooglePrettifyRhinoTest.java
 > "%JAVA_HOME%\bin\java"  -cp .;rhino-1.7R4.jar GooglePrettifyRhinoTest

#code{{
import org.mozilla.javascript.*;
//...
// ...
String txt = src.getText();
txt = txt.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
txt = txt.replace("\n", "<br />");
//...
// ...
public static String prettifyOne(String txt) {
  txt = txt.replace("\n", "<br />"); //???
  String str = "";

  //String p = "https://raw.githubusercontent.com/google/code-prettify/master/src/prettify.js";
  //String q = "http://www.envjs.com/dist/env.rhino.1.2.js";
  //try (Reader reader1 = new BufferedReader(new InputStreamReader(new URL(p).openStream()));
  //     Reader reader2 = new BufferedReader(new InputStreamReader(new URL(q).openStream()))) {
  try (Reader reader1 = new BufferedReader(new FileReader("env.rhino.1.2.js"));
       Reader reader2 = new BufferedReader(new FileReader("prettify.js"))) {

    ContextFactory contextFactory = new ContextFactory();
    Context cx = contextFactory.enterContext();
    cx.setOptimizationLevel(-1);
    cx.setLanguageVersion(Context.VERSION_1_5);
    ScriptableObject globalScope = cx.initStandardObjects();

    //String[] names = {"print"};
    //globalScope.defineFunctionProperties(names, GooglePrettifyRhinoTest.class, ScriptableObject.DONTENUM);
    String printFunction = "function print(message) {java.lang.System.out.println(message);}";
    cx.evaluateString(globalScope, printFunction, "print", 1, null);

    Scriptable scope = cx.newObject(globalScope);
    scope.setPrototype(globalScope);
    scope.setParentScope(null);

    //Global global = new Global();
    //Context cx = ContextFactory.getGlobal().enterContext();
    //global.init(cx);
    //cx.setOptimizationLevel(-1);
    //cx.setLanguageVersion(Context.VERSION_1_5);
    //Scriptable scope = cx.initStandardObjects(global);

    //cx.evaluateString(scope, "var arguments = ['envjs/rhino.js', 'prettify.js'];", "arguments", 1, null);
    //cx.evaluateString(scope, "var arguments = [];", "arguments", 1, null);

    Script envjs = cx.compileReader(reader1, "env.rhino.1.2.js", 1, null);
    envjs.exec(cx, scope);

    Script prettify = cx.compileReader(reader2, "prettify.js", 1, null);
    prettify.exec(cx, scope);

    //Object ooo = cx.evaluateString(scope, "document.createElement('div');", "<output>", 1, null);
    //System.out.println(Context.toString(ooo));

    //Object result = cx.evaluateReader(scope, r, "env.rhino.js", 1, null);
    //System.out.println(result);
    //Object result = cx.evaluateReader(scope, reader0, "", 1, null);
    //result = cx.evaluateReader(scope, reader1, "", 1, null);
    //result = cx.evaluateReader(scope, reader2, "prettify.js", 1, null);

    Function fct = (Function) scope.get("prettyPrintOne", scope);
    Object result = fct.call(cx, scope, scope, new Object[] {txt, "java", false});
    str = Context.toString(result);

  } catch(Exception ex) {
    ex.printStackTrace();
  } finally {
    Context.exit();
  }
  return str;
}
}}

** org.mozilla.javascript.EcmaError: ReferenceError: "print" is not defined. [#u9114222]
以下のようなエラーが出る場合の対処方法について。

 org.mozilla.javascript.EcmaError: ReferenceError: "print" is not defined. (env.rhino.1.2.js#1295)

- [https://groups.google.com/forum/#!msg/mozilla.dev.tech.js-engine.rhino/EJmJJhftc3g/JELu_XEi5TwJ Use Context.evaluateReader with javascript file - Google グループ] のように`public static`なメソッドを(GooglePrettifyRhinoTestに)追加

#code{{
String[] names = {"print"};
globalScope.defineFunctionProperties(names, GooglePrettifyRhinoTest.class, ScriptableObject.DONTENUM);
}}

#code{{
public static void print(String str) {
  System.out.println(str);
}
}}

- [http://ka-ka-xyz.hatenablog.com/entry/20120411/1334160190 RhinoでjQuery - ka-ka_xyzの日記] のように`org.mozilla.javascript.tools.shell.Global`を使用

#code{{
Global globalScope = new Global();
Context cx = ContextFactory.getGlobal().enterContext();
globalScope.init(cx);
cx.setOptimizationLevel(-1);
cx.setLanguageVersion(Context.VERSION_1_5);
}}

- [http://snipplr.com/view/38607/rhino-envjs-testing-example/ Rhino env.js testing example - Groovy - Snipplr Social Snippet Repository] のように、`function print`を定義

#code{{
String printFunction = "function print(message) {java.lang.System.out.println(message);}";
cx.evaluateString(scope, printFunction, "print", 1, null);
}}

** HTMLPreElement [#l0271013]
[https://github.com/google/code-prettify/blob/master/src/prettify.js prettify.js(最新)]では、`prettyPrintOne`が、以下のように`DOM`を使用するのでエラーになる。

 var container = document.createElement('pre');
 // This could cause images to load and onload listeners to fire.
 // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
 // We assume that the inner HTML is from a trusted source.

- [http://www.envjs.com/dist/env.rhino.1.2.js env.rhino.1.2.js]を追加しても、HTMLPreElement が存在しないのでエラー
-- 以下を`env.rhino.1.2.js`に追加して、改行を`br`に変更した文字列を`prettyPrintOne`に与えるとうまくいく

 txt = txt.replace("\n", "<br />");

#code{{
/*
 * HTMLPreElement - DOM Level 2
 * HTML5: 4.5.12 The Pre Element
 * http://dev.w3.org/html5/spec/Overview.html#the-pre-element
 */
HTMLPreElement = function(ownerDocument) {
    HTMLElement.apply(this, arguments);
};

HTMLPreElement.prototype = new HTMLElement();
__extend__(HTMLPreElement.prototype, {
    toString: function() {
        return '[object HTMLPreElement]';
    }
});
}}

- [https://github.com/thatcher/env-js thatcher/env-js · GitHub (1.3)] には、`HTMLPreElement`が存在するが、生成された`envjs`以下のファイルの使い方が分からない…。
-- `local_settings.js` : 空のファイルをカレントに作成しておく?
-- `arguments` : `cx.evaluateString(scope, "var arguments = [];", "arguments", 1, null);`などで空の配列を作成しておく?
-- 何も言わずに落ちる : ????

* 参考リンク [#reference]
- [https://docs.oracle.com/javase/jp/8/docs/technotes/guides/scripting/ Scripting for the Java Platform]
- [https://github.com/google/code-prettify google/code-prettify: Automatically exported from code.google.com/p/google-code-prettify]
- [https://developer.mozilla.org/ja/docs/Rhino Rhino | MDN]
- [[JEditorPaneのHTMLEditorKitにCSSを適用>Swing/StyleSheet]]
- [[JEditorPaneにソースコードをシンタックスハイライトして表示する>Swing/SyntaxHighlightingEditorPane]]

* コメント [#comment]
#comment
- 新しい`prettify.js`(`prettify-1-Jun-2011.tar.bz2`、[https://github.com/google/code-prettify/blob/0b3341b3e9105ddaecf93cc632284f8db7994faf/src/prettify.js First pass at a way to dodge newline issues in IE.]の修正が入っているバージョン)では、`prettyPrintOne`の内部で`Document`型のオブジェクトが使用されるようになっているので`Rhino`だけ(`Envjs`とか使えば良さそうなんだけど…)では実行できない。このため上記のサンプルでは古い`prettify.js`([https://github.com/google/code-prettify/blob/f5ad44e3253f1bc8e288477a36b2ce5972e8e161/src/prettify.js Fixed prettyPrintOne by removing requirement that a job have a source node. Fixes issues 133 and 134.])を参照するように変更。 -- &user(aterai); &new{2012-03-09 (金) 16:58:03};
- `JEditorPane`でのプレビューを追加。 -- &user(aterai); &new{2012-05-28 (月) 17:45:14};
- `1.8.0`の`Nashorn`を使うとなぜか文字のハイライトがずれる? -- &user(aterai); &new{2013-07-21 (日) 00:21:18};
-- 詳細は不明だが(`regex`あたりのバグ?)、[http://download.java.net/jdk8/changes/jdk8-b100.html jdk8-b100]では正常に動作するようになった。 -- &user(aterai); &new{2013-08-01 (木) 19:20:09};

#comment