TITLE:Rhinoでgoogle-prettify.jsを実行する

Posted by aterai at 2011-01-25

Rhinoでgoogle-prettify.jsを実行する

#adsense2

Rhinoでgoogle-prettify.jsを実行し、ソースコードをハイライトされたHtml(google sites用)に変換します。

GooglePrettifyRhinoTest.png

サンプルコード

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 = "http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js";
    String p = "http://google-code-prettify.googlecode.com/svn-history/r120/trunk/src/prettify.js";
    //String p = "http://terai.xrea.jp/skin/irid/prettify.js";

    try (Reader reader = new BufferedReader(
        new InputStreamReader(new URL(p).openStream()))) {
      engine.eval("var window={};var 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);
  }
}

解説

上記のサンプルでは、new ScriptEngineManager().getEngineByName("JavaScript"); で取得したJavaScriptエンジン(Rhino)に、prettify.jsを読み込んで、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
import org.mozilla.javascript.*;
//...
String txt = src.getText();
txt = txt.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
String str = "";

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

  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);

  Scriptable scope = cx.newObject(globalScope);
  scope.setPrototype(globalScope);
  scope.setParentScope(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 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);
  // ScriptableObject w = (ScriptableObject)scope.get("window", scope);

  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();
}
editor.setText(pre+str+"\n</pre>");
//...

org.mozilla.javascript.EcmaError: ReferenceError: "print" is not defined.

以下のようなエラーが出る場合の対処方法について。

org.mozilla.javascript.EcmaError: ReferenceError: "print" is not defined. (env.rhino.1.2.js#1295)
String[] names = {"print"};
globalScope.defineFunctionProperties(names, GooglePrettifyRhinoTest.class, ScriptableObject.DONTENUM);
public static void print(String str) {
  System.out.println(str);
}
Global globalScope = new Global();
Context cx = ContextFactory.getGlobal().enterContext();
globalScope.init(cx); 
cx.setOptimizationLevel(-1);
cx.setLanguageVersion(Context.VERSION_1_5);
String printFunction = "function print(message) {java.lang.System.out.println(message);}";
cx.evaluateString(scope, printFunction, "print", 1, null);

HTMLPreElement

prettify.js(最新)env.rhino.1.2.jsで、

var container = document.createElement('pre');

がエラーになる。

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

参考リンク

コメント

  • 新しいprettify.js(prettify-1-Jun-2011.tar.bz2)では、prettyPrintOneの内部でDocument型のオブジェクトが使用されるようになっているのでRhinoだけ(Envjsとか使えば良さそうなんだけど…)では実行できない。このため上記のサンプルでは古いprettify.js(このサイトで使用中のprettify-21-Jul-2010.zip)*1を参照するように変更。 -- aterai
  • JEditorPane でのプレビューを追加。 -- aterai