はじめに
Java 8 の、REPL の作成2回目となります。
目的や注意点に関しては、1回目の記事を参照して下さい。
それでは早速、ソースの説明に入ります。
コンソールインターフェース
Windows の cmd や、Eclipse のコンソールへのアクセスを提供するインターフェースです。
-
input()
コンソールからの入力受付へのインターフェース。 -
print()
画面出力メソッドのインターフェース。 -
cleanup()
Interface.Finalizable インターフェースの持つメソッドへのインターフェースです。
Interface.Finalizeable インターフェースは、後日説明します。
interface Interface { ... }
と囲っているのはミスではなく、ダミーのインターフェースです。
細々としたインターフェースを単独ファイルとして作成すると、ファイルシステムが煩雑になる為、この中にインターフェースを追加していきます。
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()
で生成して受け渡すだけです。
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 で静的に生成したコンソールオブジェクトを渡すことを想定していましたが、インスタンス化して渡すように変更した為、初期化をコンストラクターに移動しています。
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に組み込むクラスの作成が続きます。