0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

REPL(?) 作成への道 (2)

Last updated at Posted at 2018-10-28

はじめに

Java 8 の、REPL の作成2回目となります。
目的や注意点に関しては、1回目の記事を参照して下さい。

それでは早速、ソースの説明に入ります。

コンソールインターフェース

Windows の cmd や、Eclipse のコンソールへのアクセスを提供するインターフェースです。

  • input() コンソールからの入力受付へのインターフェース。
  • print() 画面出力メソッドのインターフェース。
  • cleanup() Interface.Finalizable インターフェースの持つメソッドへのインターフェースです。
    Interface.Finalizeable インターフェースは、後日説明します。

interface Interface { ... } と囲っているのはミスではなく、ダミーのインターフェースです。
細々としたインターフェースを単独ファイルとして作成すると、ファイルシステムが煩雑になる為、この中にインターフェースを追加していきます。

Interface.java
package console;

interface Interface {
    /** console interface. */
    interface Console extends Interface.Finalizable {
        // methods for input string from console ======================================
        default String input(String prompt);

        // methods for output string to console =======================================
        void print(String format, Object... args);
        void print(String s);

        // extends Inteeface.Finalizeable =============================================
        void cleanup();
    }
    // ... more interface ...
}

コンソールサービス

このファイルも詰め込んでいます...。
具象コンソールクラスは、Console.create() で生成して受け渡すだけです。

Service.java
package console;

public interface Service {
    class Console {
        public  static final String newLine = System.lineSeparator();
        private static final String defaultEncoding = "UTF-8";

        public  static Interface.Console create(String encoding) {
            if ( encoding == null  ||  encoding.trim().isEmpty() ) { 
                encoding = defaultEncoding; 
            }
            if ( !java.nio.charset.Charset.isSupported(encoding) ) {
                encoding = defaultEncoding; 
            }
            return    (System.console() != null)
                    ? new SystemConsole(System.console())
                    : new StandardConsole(encoding);
        }

        // ///////////////////////////////////////////////////////////////////////////////
        /** console uses standard in/out class.
         */ // ///////////////////////////////////////////////////////////////////////////
        private static class StandardConsole implements Interface.Console {
            protected StandardConsole(String encoding) {
                try {
                    java.io.InputStreamReader istream = new java.io.InputStreamReader(System.in, encoding);
                    reader_  = new java.io.BufferedReader(istream);
                    out_     = new java.io.PrintStream(System.out, true, encoding);
                } catch (java.io.IOException e) {
                    System.out.println( e.getMessage() );
                }
            }

            // methods for input string from console ===============================
            public String input(String prompt) {
                out_.printf(prompt);
                String s = "";
                try {
                    s = reader_.readLine();
                } catch (java.io.IOException e) {
                    System.out.println( e.getMEssage() );
                }
                return s;
            }

            // methods for output string to console ================================
            public void print(String format, Object... args) { out_.printf(format, args); }
            public void print(String s)                      { out_.println(s);           }

            // implements Finalizable.cleanup() ====================================
            public void cleanup() {
                out_.close();
                out_ = null;
                try {
                    reader_.close();
                    reader_ = null;
                } catch(java.io.IOException e) {
                    System.out.println( e.getMessage() );
                }
            }

            // internal fields =====================================================
            java.io.BufferedReader reader_;
            java.io.PrintStream    out_;
        }

        // ///////////////////////////////////////////////////////////////////////////////
        /** console uses System.console() class.
         */ // ///////////////////////////////////////////////////////////////////////////
        private static class SystemConsole implements Interface.Console {
            protected SystemConsole(java.io.Console con) {
                console_ = con;
            }

            // methods for input string from console ===============================
            public String input(String prompt) { return console_.readLine(prompt); }

            // methods for output string to console ================================
            public void print(String format, Object... args) {
               console_.printf(format, args); 
            }
            public void print(String s) {
                console_.printf(s + Service.Console.newLine); 
            }

            // implements Finalizable.cleanup() ====================================
            public void cleanup() { /* no operation */ }

            // internal fields =====================================================
            java.io.Console console_;
        }
    }
}

説明

Service.Console

サービスとついていますが、実際には、Console のファクトリークラスです。
コンソールのエンコーディング名を受け取り、それを元に具象コンソールクラスを生成して返します。
不正なエンコーディング名が渡された場合は、デフォルトの "UTF-8" をセットします。

  • System.console() (java 1.6 以降)が使用できる場合、こちらを利用したコンソールオブジェクト(SystemConsole)を返します。
  • System.console() が使用できない場合は、標準の入出力(InputStreamReader と PrintStream)を利用したコンソールオブジェクト(StandardinConsole)を返します。
    通常のアプリケーションとして使用する場合、標準入出力のコンソールは使用されませんが、Eclipse や IntelliJ 等で実行した際に使われるコンソールウィンドウでは System.console は使用できないd為、こちらが使用されます。
public interface Service {
    class Console {
        public  static final String newLine = System.lineSeparator();
        private static final String defaultEncoding = "UTF-8";

        public  static Interface.Console create(String encoding) {
            if ( encoding == null  ||  encoding.trim().isEmpty() ) { 
                encoding = defaultEncoding; 
            }
            if ( !java.nio.charset.Charset.isSupported(encoding) ) {
                encoding = defaultEncoding; 
            }
            return    (System.console() != null)
                    ? new SystemConsole(System.console())
                    : new StandardConsole(encoding);
        }
        // ... inner class ...
}

SystemConsole

Sysyem.Console を利用したコンソールクラスです。
単純に、処理を委譲しているだけですので、特に説明は不要かと思います。
上の Service.Console で、一所懸命、エンコーディングのチェックを行っていますが、こちらでは、実際にコンソール(cmd.exe など)で使用しているエンコーディングが初期設定されている為、使用しません(見た所、変更する方法もなさそうです)。

public interface Service {
    class Console {
        // ... 省略 ...

        // ///////////////////////////////////////////////////////////////////////////////
        /** console uses System.console() class.
         */ // ///////////////////////////////////////////////////////////////////////////
        private static class SystemConsole implements Interface.Console {
            protected SystemConsole(java.io.Console con) { console_ = con; }

            // methods for input string from console ===============================
            public String input(String prompt) { return console_.readLine(prompt); }

            // methods for output string to console ================================
            public void print(String format, Object... args) {
               console_.printf(format, args); 
            }
            public void print(String s) {
                console_.printf(s + Service.Console.newLine); 
            }

            // implements Finalizable.cleanup() ====================================
            public void cleanup() { /* no operation */ }

            // internal fields =====================================================
            java.io.Console console_;
        }
    }
}

StandardinConsole

こちらは、標準入出力を使用したコンソールクラスです。
SystemConsole と同じことをやっているだけですが、ボイラープレートのおかげでコード量2倍となっています。
それでもファクトリーでエンコーディングチェックを行ってりる分、記述量は減っています。

  • StandardConsole(String) 引数で受け取ったエンコーディング名を BufferReader、PrintStream にセットしています。
  • input(String) プロンプトを表示し、BufferdReader.readLine() から受け取った文字列を返すだけのはずですが、2行で済むはずのコードが大変なことに...。こういうところが鬱陶しくてキライ
  • print() これは、PrintStream に転送しているだけです。
  • cleanup() Interface.Finalizable のインターフェースメソッドの実装です。BufferdReader と、PrintStream とで同じような処理をしてるのに、片方だけ例外チェックが必要とか...。こういうところが(ry
public interface Service {
    class Console {
        // ... 省略 ...

        // ///////////////////////////////////////////////////////////////////////////////
        /** console uses standard in/out class.
         */ // ///////////////////////////////////////////////////////////////////////////
        private static class StandardConsole implements Interface.Console {
            protected StandardConsole(String encoding) {
                try {
                    java.io.InputStreamReader istream = new java.io.InputStreamReader(System.in, encoding);
                    reader_  = new java.io.BufferedReader(istream);
                    out_     = new java.io.PrintStream(System.out, true, encoding);
                } catch (java.io.IOException e) {
                    System.out.println( e.getMessage() );
                }
            }

            // methods for input string from console ===============================
            public String input(String prompt) {
                out_.printf(prompt);
                String s = "";
                try {
                    s = reader_.readLine();
                } catch (java.io.IOException e) {
                    System.out.println( e.getMessage() );
                }
                return s;
            }

            // methods for output string to console ================================
            public void print(String format, Object... args) { out_.printf(format, args); }
            public void print(String s)                      { out_.println(s);           }

            // implements Finalizable.cleanup() ====================================
            public void cleanup() {
                out_.close();
                out_ = null;
                try {
                    reader_.close();
                    reader_ = null;
                } catch(java.io.IOException e) {
                    System.out.println( e.getMessage() );
                }
            }

            // internal fields =====================================================
            java.io.BufferedReader    reader_;
            java.io.PrintStream       out_;
    }
}

BaseREPL の修正

エンコーディング名を渡す必要があることから、前回の BaseREPL も修正となります。
ここでは、修正箇所のみ記載いたします。全コードは前回の記事を参照してください。

  • BaseREPL() コンストラクターで、コンソールに渡すエンコーディング名を受け取るようにしました。
    当初の予定では、Service.Console で静的に生成したコンソールオブジェクトを渡すことを想定していましたが、インスタンス化して渡すように変更した為、初期化をコンストラクターに移動しています。
BaseREPL.java
class BaseREPL {
    BaseREPL(String encoding) {
        if ( encoding == null  ||  encoding.trim().isEmpty() ) { encoding = "UTF-8"; }
        console_ = Service.Console.create(encoding);
    }
    // ... 途中省略 ...
    // internal fields =============================================================
    protected final Interface.Console console_;
}

まとめ

今回は、REPLが利用するコンソールの説明でした。
当初はコンパクトだったのですが、あれこれ修正している間に、それなりのボリュームになってしまいました。

もう数回は、REPLに組み込むクラスの作成が続きます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?