プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

Eclipseプラグイン開発: バンドルリソース関連

Eclipse プラグイン開発 目次



 IPath,IResourceなどで、Eclipseプラットフォームが管理するリソースにアクセスが出来ます。

 しかし、プラグインがアクセスするのは何もユーザーが準備するファイルだけではありません。プラグイン自身が始めから持っているファイル達も利用する場面があります。今回の話題はそういったファイルの扱い方です。

バンドルリソースの場所

 プラグイン開発をし、実際に一般に使えるようにした場合、プラグイン用に用意したリソースが配置される場所は2通りあります。

  • 配布jarの中に入ったまま
  • インストール時に自動的に展開される


 インストール後、展開されるかどうかはfeatureの設定によりますが、基本的には後者は「exe」などの実行ファイルや、外部ライブラリの「jar」等を格納するために使い、画像などのファイルはjarの中に入れたままにすることが多いようです。


 展開されたリソースはjava.io.Fileやjava.nio.Pathで処理可能ですが、jarに同梱されているリソースはFileやPathでは処理できません。どちらのインストール方法でも対応できるように基本的にはバンドルのリソースはURLで操作することになります。


バンドルリソースの取得

 では実際にプラグインに梱包されたリソースを読み出してみましょう。例として次の様な内容のプラグインを作成しました。

f:id:nodamushi:20170405023654p:plain

 mytest.Activatorはプラグインを作ったら出てくるコードそのままです。ブログ掲載のためにコメントと空白を消しただけで、特にプログラムを弄ってはいません。

package mytest;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {
  private static BundleContext context;
  static BundleContext getContext() {return context;}
  public void start(BundleContext bundleContext) throws Exception {
    Activator.context = bundleContext;
  }
  public void stop(BundleContext bundleContext) throws Exception {
    Activator.context = null;
  }
}

 


 一番単純なリソースの取得方法は、このActivatorに渡されるBundleから直接リソースURLを取得する方法です。存在しないリソースを取得しようとした場合はnullが返ります。

  //ResourceTest.javaの中身
  Bundle bundle = mytest.Activator.getContext().getBundle();

  @Test public void getResourceTest(){
    assertThat( bundle.getEntry("/resource/alphabet.txt"),notNullValue());
    //存在しないときはnull
    assertThat( bundle.getEntry("/resource/hoge.txt"),nullValue());
  }

 


 URLを取得できてしまえば、中身を読み出すには普通にopenStreamしてしまえば良いので簡単ですね。

  //ResourceTest.javaの中身
  private BufferedReader reader(InputStream i){
    return new BufferedReader(new InputStreamReader(i));
  }

  @Test public void readResourceTest() throws IOException{
    URL entry = bundle.getEntry("/resource/alphabet.txt");
    try(BufferedReader r= reader(entry.openStream())){
      assertThat(r.readLine(),is("abcdefghijklmnopqrstuvwxyz"));
    }
  }

 


 これでも基本的には問題ないのですが、フラグメント・プロジェクトを扱うと、これではファイルが取得できないという問題があります。そういった問題も考慮し、お行儀良くURLやInputStreamを取得する場合は、FileLocatorを使います。

 とりあえず、今はこれだと困ることがある、ということだけ理解してください。詳しくはフラグメント・プロジェクトの項で解説します。

  //ResourceTest.javaの中身
  Path path = new Path("/resource/alphabet.txt");
  @Test public void getResourceTest2(){
    URL url = FileLocator.find(bundle, path, null);
    assertThat(url,notNullValue());
  }

  @Test public void readResourceTest2() throws IOException{
    try(BufferedReader r=
        reader(FileLocator.openStream(bundle,path, false))){
      assertThat(r.readLine(),is("abcdefghijklmnopqrstuvwxyz"));
    }
  }

 

URLをFileやPathに変換する際の要注意事項

 URLをjava.io.Fileやjava.nio.file.Pathに変換する場合に注意する点があります。 (以降この節ではFile,Pathと省略。EclipseのPath (org.eclipse.core.runtime.Path)ではない)

 ネイティブ実行ファイル(exeなど)をプラグインに含む場合、インストール方法を展開にします。こうすると、実行ファイルがファイルとして展開されて保存される為、「jarから取得→一時ファイルとして保存→実行」をしなくても、パスさえ取得出来れば即座に実行出来ます。この時、FileやPathとしてパスを取得すると、JavaのAPIと関連させる場合に便利です。

 一般的には、URLからURIに変換し、そのURIからFileやPathを得ます。その時にはtoURIを使わずに以下の様にしてください。

URL  url  = FileLocator.find(bundle, path, null);
URI  uri  = new URI(url.getProtocol(), null, url.getPath(), null);
File file = new File(uri);
Path path = Paths.get(uri);


 URL.toURIを使うと、URLが特定の文字を含む場合、例外が発生します。
 問題の核は、URLがRFC2396に従って適切なエスケープがされている必要があるという点です。例えば空白文字は%20、%そのものは%25などの様にエスケープする必要があります。このエスケープ処理はURLを生成するクラスが責任を持ちます。これらの詳細はURLのJavaDocを読んでください。

 Eclipseが作成するURLは、空白文字列を含む場合は空白をそのまま空白として出力します。従って、toURIをそのまま使うと、例外が発生する可能性があります。



バンドルリソースの画像ファイルの取得

 Eclipseプラグイン開発において、画像はImageのまま保持するのではなく、ImageDescriptorの形で保持しておいて、データを使い回すと言うことが多いです。

 このImageDescriptorを作るのは割と面倒なんですが、AbstractUIPluginというクラスのstaticメソッドとして便利関数が定義されているのでこれを使います。URLのときと同様に、存在しない画像ファイルの場合はnullが返ります。

  //ResourceTest.javaの中身
  @Test public void getImageTest(){
    ImageDescriptor img = AbstractUIPlugin.imageDescriptorFromPlugin(bundle.getSymbolicName(), "/resource/test.jpg");
    assertThat(img,notNullValue());

    ImageDescriptor img2 = AbstractUIPlugin.imageDescriptorFromPlugin(bundle.getSymbolicName(), "/resource/hoge.jpg");
    assertThat(img2,nullValue());
  }


 といっても、AbstractUIPlugin~~~~って毎度書くのも面倒くさいし、何のプラグインの画像読み出してるのかわかりにくいので、私はこうしています。もっと真面目に実装する場合はリソースマネージャーとか自作するみたいよ。私はしてないけど。

  //Activator.javaに追加
  /** このプラグインのID */
  public static final String PLUGIN_ID="mytest";

  /**
   * このプラグインのリソースからImageDescriptorを取得
   * @param path 画像ファイルのパス
   * @return
   */
  public static ImageDescriptor getImageDescriptor(String path){
    return AbstractUIPlugin.imageDescriptorFromPlugin(PLUGIN_ID, path);
  }

 

共通画像の取得

 Eclipseプラグインを開発する上で、大概必要になるフォルダアイコンやらを一々用意するのは面倒です。むろん、AbstractUIPlugin.imageDescriptorFromPluginで他のプラグインの持っている画像を取得することは出来ることは出来ますが、画像ファイルのパスが分からんし、調べたとしても(調べるプラグインがあります)、そのパスがずっと有効かどうかなんて分かりません。

 そんな開発者のために、一般によく用いられるアイコンに関しては、取得出来るようにしてくれてある場合があります。

ISharedImages shared = PlatformUI.getWorkbench().getSharedImages();
//ファイルアイコン
shared.getImageDescriptor(ISharedImages.IMG_OBJ_FILE);

//Java画像も公開されています
JavaUI.getSharedImages().getImageDescriptor(org.eclipse.jdt.ui.ISharedImages.IMG_OBJS_CLASS);

//CDTも公開しています
CDTSharedImages.getImageDescriptor(CDTSharedImages.IMG_OBJS_BUILD);

Eclipseプラグイン開発: リソースパス関連

Eclipse プラグイン開発 目次


 Javaでファイル、ディレクトリを操作する場合、java.io.Fileやjava.nio.file.Pathを用いて表現します。

 EclipseではIPathないし、IResourceを用いて表現するのですが、これもうほんと、使いにくい。
 といっても、Eclipseでファイルを扱おうと思ったら避けることは出来ませんので、理解して付き合っていくしかありません。

IPath

 IPathはJavaのAPIで言えば、java.nio.file.Pathとほぼ同等の機能です。ファイルやディレクトリなどの場所(パス)を表現します。

 Eclipseプラグイン開発において、IPathは主に以下のパスを表現します。

  • フルパス:ワークスペースをルートとする、ワークスペース空間における絶対パス表現
  • ロケーション(ローカルシステム)パス:OS固有の絶対パス表現
  • 相対パス:絶対パスではないパス表現


f:id:nodamushi:20170403014824p:plain:right

 フルパスというのは、ワークスペースをルートとする、Eclipseシステム系の絶対パスを示します。
 たとえば、右図の様なワークスペースのプロジェクト構造をしているとき、AプロジェクトのA.javaは「/A/src/A.java」と表現され、BプロジェクトのdlinkフォルダのTest.classは「/B/dlink/Test.class」と表現されます。
 ルートであるワークスペースのパスは「/」です。この辺はUnixと同じ表現ですね。@でも何でも良いから別な文字にしときゃ良かったのに。

 

 一方で、ロケーションパスというのはOSのファイルシステムにおける絶対パスを表します。
 実際にフルパス、ロケーションパスを見たければ、プログラムを書かなくても、プロパティビューから確認することが出来ます。
 右図のワークスペースは、「F:\workspace」に配置しているのですが、プロジェクトAとプロジェクトBのローカルパスを表示すると下図の様に「F:\workspace」とは全然関係ないところに配置されていることが分かります。
 従って「ワークスペースのディレクトリ(F:\workspace)」+「フルパス」としてもOS上のファイルパスを取得することが出来るとは限りません。
f:id:nodamushi:20170403015926p:plain


 なお、ロケーションパスには、生ロケーションパス(Raw Location Path)が存在しますが、これは使うことないと思います。Eclipseのフォルダリンクは、リンク先を絶対パスで指定するタイプのものと、変数で指定する仮想リンクがあるのですが、生ロケーションパスが表現するのは後者のほうです。例えば、Test.javaはこんな風に出てきます。「PARENT-1-PROJECT_LOC/java/Test.java」。普通にロケーションパスを取得すれば、変数の所は解決してくれるので、考える必要はありません。

 相対パスは、Bundle内のリソースパスだったり、特定のパスからの相対パスだったり等、使う場所や条件によって意味が変わる通常の相対パスです。

IResource

 IResourceはワークスペース空間におけるリソース(ワークスペース、プロジェクト、フォルダ、ファイル)を表現するインターフェースです。ワークスペース空間におけるリソースというのは、要するにフルパスで表現できるリソースを表します。
 JavaのAPIでいうと、パス以外の付加情報や操作機能を持っているという点でjava.io.Fileがよく似ています。

 java.io.Fileと違うのは、ルート(ワークスペース)、プロジェクト、フォルダ、ファイルと4種類のインターフェースに派生する点と、IFile(ファイルを表現するIResource)から内容を編集したときに、Eclipseのローカル履歴に変更情報を残すことが出来る点でしょう。

IResource 説明 取得方法
IWorkspaceRoot ワークスペースを表す IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IProject プロジェクトを表す IProject project = root.getProject("B");
IFolder フォルダーを表す container.getFolder(new Path("hoge"));
IFile ファイルを表す container.getFile(new Path("hoge.txt"));


 containerはIWorkspaceRootか、IProject、IFolderのことです。IContainerのメソッドの引数として渡すIPathは、containerからの相対パスです。

 getProject,getFolder,getFileによるプロジェクト、フォルダー、ファイルの取得は、存在しないリソースも表します。この辺はもうAPI設計のミスだと思うわー。newProject,createFolderとかにして、存在する場合はエラーにすべきだと思うわー。

 IProject、IFolder、IFileに関わらず、存在する場合のみIResourceを取得したい場合は、container.findMemberメソッドを使います。
 

IPathからjava.io.Fileやjava.nio.Pathへの変換

 Eclipse内部だけで完結するならIPath,IResourceで全て処理すれば良いのだが、大概そうもいかない。普通のライブラリって、ファイル表現にFileだったりPathを使うわけで、IPathやIResourceのままだと困るのだ。

 結論を言うと、IPathやIResourceから確実にFileやPathに変換する方法はない。
 基本は「IPath→IResource→FileやPath」という変換手順を辿らなければならないが、「IPath→IResource」の段階で絶対の手順というものはないし、「IResource→FileやPath」も必ず成功するとは限らない。

IPath→IResource

 まずは渡されたIPathがフルパスなのか、ロケーションパスなのか、相対パスなのか考えなくてはならない。相対パスの場合は、何に対して相対なのかで話が変わるから、ここでは考えない。
 
 フルパスなのか、ロケーションパスなのかについては、Windowsの場合は楽です。「~:\」ないし「\\」から始まっていたらロケーションパスです。そのままFileやPathに変換しましょう。しかし、これがUnix系の場合だったら…どうすんだろうね。


 で、フルパスからIResourceに変換する一番簡単な方法は以下の方法だと思う。

IResource resource = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path("パス"));

 なお、パスが示す先がFolderであっても、ファイルとしてアクセスするような処理をしない限り、実はgetFileで特に問題ない。

 findMemberだと、存在しないファイルのパスを解決できないので、getFileを使うと良い。

IResource→File,Path

 単純には以下のようにすれば良い。しかし、プロジェクトを閉じている場合や、リンクを解決できない場合などで、getLocationはnullが返ることがあるので、要注意

File file = resource.getLocation().toFile();
Path path = Paths.get(resource.getLocation().toOSString());

Eclipseプラグイン開発: 開発環境の準備

Eclipse プラグイン開発 目次




 え?Eclipseプラグイン開発するつもりなんですか?まだ引き返せますよ?上司にVisual Studio Codeを提案した方がいいと思うよ?

 ちゃんと私は止めましたからね?

開発に必要なツール

  • 根性と泣かない心
  • GoogleとGithub
  • Eclipse IDE for Eclipse Committers
  • EASE
  • Maven

根性と泣かない心

 分からなくても泣かない。案外数ヶ月後にもう一度やってみたら分かったりする。

GoogleとGithub

 Eclipse開発で困難を極めるのは、目的を達成するために「何をどうすれば良いのか分からない」ことが多々あると言うことです。結論が「~~Interfaceを実装すれば良い」というだけのことであっても、「~~」にたどり着くのが結構大変。いや、マジで。

 とにかく、コーディング能力よりも検索能力が試されます。従ってGoogle先生は必須です。

 そして、GithubにはEclipse本体含む、多くのプラグインが公開されています。他のプラグインがどういう実装をしているのかを調べることは、非常に有用であり、重要な手がかりになります。私の場合、Githubの検索である程度当たりを付け、実際のソースコードはEclipse上で表示する、というやり方に落ち着きました。

参考URL

 増えてくると別ページに分離するかも。

 Eclipse Documentation HelpのPlatform Plug-in Developer Guide項目とひしだま氏のEclipseプラグイン開発はブックマークしておきましょう。
 ただ、Eclipse Helpのページ、サイドバーとかJavaScriptで動いてて非常に使いにくいんで、JavaScriptをoffにして目的のページだけ表示するようにした方が良いと思います。あと、ひしだま様は神様。

※EclipsepediaのSnippetが動かなくても泣かないコト

参考書籍


Eclipse Plug-in Development: Beginner's Guide - Second Edition (English Edition)

Eclipse Plug-in Development: Beginner's Guide - Second Edition (English Edition)

 Eclipseプラグイン開発そのものよりも、どっちかっていうとMavenのtychoの使い方を知るのにとても有用だった
 

Eclipse IDE for Eclipse Committers

 Eclipse Packages | The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 350 open source projects...からEclipseプラグインを開発する用のEclipseを用意しましょう。むろん、すでにEclipseを使っていて、それにプラグイン開発用プラグインをインストールしても構いません。

 特に気にしない場合、最新バージョンを選べば良いですが、対象のEclipseバージョンを限定、ないし最低バージョンが指定されている場合は、そのEclipseを選びましょう。APIが変わったり増えたりします。

EASE

 Eclipse Advanced Scripting Environment | The Eclipse Foundation

 Eclipseのプラグインで、Eclipseを実行している環境上でJavaScriptなどのスクリプト言語を動かすことが出来るプラグインです。ブラウザで言う、Firebugみたいな感じで私は使っていますが、プラグイン開発に慣れた人なら、EASEを使って、プラグインにしなくても高度なことがスクリプト的に実行できるとても便利なツールです。具体的には後に載せるYoutubeの動画を見ると分かりやすいと思います。

 Eclipseのプラグインを開発しているときに、APIがどういう動作をするのか説明読んでもよく分からないってことが結構ありますが、その動作確認のために、何度もEclipseを立ち上げ直すのはかなり面倒くさいです。EASEを使うと、そういったAPIの動作をその場で確認できます。

 ただ、v0.3まではかなり動作が不安定っていうか、しょっちゅうエラー出てました。v0.4でわりと結構使えるようになりましたが、それでも何かけっこー固まる。SWTのインスタンスにDisplayのThread以外からアクセスしようとすることが多々あるのよね…

 まだ現状動作が不安定なので、開発しているEclipseとは別のEclipseか、開発中のプラグインをテストしているEclipseで動かすのが無難でしょう。
 

Maven

Maven – Welcome to Apache Maven
Apache Maven3 (3.2.5) インストール手順 (Windows) | WEB ARCH LABO

 最近はMavenも廃れてGradleかと思うのですが、残念ながらGradleでのやり方知りません(オイ

 プラグインをビルドしたり配布する場合、Eclipseの機能をそのまま使っても良いですが、Mavenを使うことをお勧めします。特に、32bit環境、64bit環境、OS等によって配布するプラグインを変えたい場合はMavenが必須になります。


 

Eclipseプラグイン開発: 目次

 はい。皆さん。Eclipse使ってますか?え?IntelliJ?デスヨネー

 そんな、もう寂れたEclipse。もうプラグイン作ろうなんて人はこの世にいないんじゃないのかと思うけど、何故か作る羽目になったので、そのドキュメントを残しておこうと思います。

これからEclipseプラグイン開発を始めようと思う人へ

 悪いことは言わない。引き返せ。

 IntelliJか、Visual Studio Codeを先に検討した方がいいでしょう。個人的にはVS Codeが良いと思います。

目次(と言う名のTODOリスト)

 思いつく限り挙げてみたけど、以下の目次が完成することはあるんですかね?ていうか、たぶんまだ増えるんだけど。
 完成したら本にして良いんじゃないんですかねぇ。出版社の方、このクソニートをどこか拾ってくれませんかー

 なお、順番通りに記事にするとは限らない。

  1. 開発環境の準備
  2. 基礎知識
    1. OSGi
    2. プラグインプロジェクトとフラグメントプロジェクト
    3. IAdaptable
    4. パス関連(IPath,IResource等)
    5. バンドルリソース関連
    6. 非同期実行
  3. テストのやり方
    1. 非UIプラグインのテスト
  4. SWT基礎知識
    1. Binding
    2. Layout
  5. 拡張ポイント
    1. 拡張ポイントの定義
    2. 拡張ポイントの実装
  6. ビュー(パート)作成
    1. ビューの作成
    2. ツールバーの追加
    3. アクションツールバーに追加
    4. ステータスラインに追加
    5. 色、フォント設定
  7. 設定画面拡張
  8. メニュー拡張
    1. メニューバーに新規項目追加
    2. メニューバーの項目に新規アイテム追加
    3. ポップアップメニューに新規項目追加
    4. 他のプラグインのアイテムを無効化
  9. エディタ作成
    1. パーティション
    2. Position
    3. シンタックスハイライト
    4. コンテンツアシスト
    5. TM4Eによるエディタ開発
  10. プロジェクトの定義
    1. IProjectとIProjectDescription
    2. Nature
    3. プロジェクトアイコン
    4. プロジェクト設定ファイル
  11. パースペクティブ
  12. デバッガ作成
    1. ILaunchConfigurationを定義
    2. LaunchとLaunchDelegate
    3. LaunchDelegateを起動する
      1. Launch Configuration Tabから起動
      2. Launch Shortcutから起動
    4. IDebugTargetの定義
  13. Eclipse CDTの拡張
    1. 基礎知識
      1. ICProject
      2. Configuration
      3. SourceEntry
    2. ビルド
      1. インターナルビルダー
      2. ツール
      3. ツールチェイン
      4. ビルドマクロ

スマホがパソコンに取って代わるべきではない理由

wpb.shueisha.co.jp

 上記の記事を読んだからという訳ではないですが、私は常々スマホはパソコンに取って代わるべきではないと思っています。

 取って代われ「ない」ではなくて、取って代わる「べきではない」です。技術的には近い将来、取って代わられるでしょう。(むろん、全てが、とは言いません。)

距離とダメージ

 さて、私が取って代わるべきでない、と考える理由はデバイスとの「物理的な距離」です。

 技術が発展するにつれて、計算機と人間の物理的な距離はドンドン近くなっています。最初はどでかい建物だった計算機がいつの間にか卓上に乗るようになり、気がつけば手に乗り、腕時計や眼鏡として体に密着するようになってきました。最終的に行き着く距離は0どころか、人間の体内に埋め込む同一化でしょう。

 同一化まで技術が進歩した場合どうなるのかは分かりませんが、現状の技術では、どうしても「眼」への負担が大きくなってきています。

 私なんかは

  • 遠視
  • 弱視
  • 斜視
  • 乱視
  • 不同視
  • 飛蚊症
  • ドライアイ

と、眼の異常のオンパレードなので、よく分かるんですが、スマホって1時間も弄ってると眼へのダメージがやばいです。あぁ、後、眼圧も昔から若干高いそうで。もう、吸血鬼みたく目からビーム出したり、邪気眼の1つぐらい生えててもおかしくないんじゃないんですかね。

 昔から、近いモノを見続けることは、目に良くないと言われています。特にスマホの距離は乱視とドライアイに大ダメージで、うっかりスマホゲーとかぽちぽちやってると、その後2,3時間はぶれまくって、まともに見えなくなります。PCは仕事で10時間ぐらいまでなら弄っててもそこまでじゃない(10時間を越えると視界がやばい)んですが、スマホなら大体30分ぐらいが視界維持の限界です。Surfaceとかベッドに寝っ転がりながらよく弄っているのですが、文字を大きくして使っていても、2時間も弄ると眼がやばいです。


スマホに取って代わられると長時間使用が強制になる

 PCやTV、ゲームは目に悪い。そんなの随分と昔から言われてきたことで、今更な話です。

 しかし、だからといって、「健康のため、我が社は社員にPCをさせません」という会社があるでしょうか?無いですよね。多くの人がドライアイ等に悩みながら、長時間のPC作業を強制されているはずです。

 今やPC市場は斜陽産業で、生産数が落ちていけばスマホとPCの値段が逆転することもいずれ起こるでしょう。経費の問題からPCからスマホに移行ということもあるかもしれません。そうなると、PCと同様に、誰もが長時間スマホを使用することが強制されるはずです。

 こうなってくると、今は特にスマホ操作で異常を感じていない健常な眼を持っている人であっても、おそらく私と同じ症状が出てくるんじゃないかと思います。もしかすると、近距離でも目に優しいデバイスってのが出てくるかも知れませんが、そんなにすぐに出てくるとは思えません。(網膜に直接像を結ぶとか?)


 これを回避する手段として、スマホの外部端子を充実させて、周辺デバイスを利用するということが考えられます。しかし、経費削減を求める昨今の企業で、スマホが一つあれば仕事できるのに、追加のモニタやらキーボードやらを社員に配ってくれるってことはまずないでしょう。私の眼の悪さを理由に巨大なモニタを頂いてはいますけど、未だにSSDにはしてくれないんだよなぁ。(27インチディスプレイを1mは離して、フォントサイズ13以上じゃないと読む気がしない。一応、小さい文字も、まだ若いので眼筋を酷使すれば見えることは見えるけど、普通の人だったら15cmぐらいの距離の文字を読むぐらいには力を入れないといけないのです。)

PCスキルを身につけたらいいんじゃないかなぁ

 スマホとPCのどっちが効率的か?これについては、流石に2017年現在の今はまだPCの方に軍配が上がると思います。上がるんじゃないかなぁ?いや、正直なところ私がスマホ使わないから、よく分からんケド。

 でも、そのうちスマホもPCも大して効率に差が無いか、逆転するという職種も増えてくるでしょう。とはいえども、紙と計算機に比べれば、スマホとPCにはそこまで大きな効率差はないんじゃないでしょうか。

 ならばスマホにPCが取って代わられようとしたとき、我々の眼を守ってくれるのは、スマホよりもPCの方が圧倒的に効率よく作業できるというユーザーのPCスキルです。スマホにした所為で効率が下がった、PCの方がスキル的に効率が良いとなると、経営者も一考せざるを得ないでしょう。

 といっても、昨今PCが使えない若者とか、第二のディジタルディバイド世代とか言われているように、スマホが主流で、PCが使えない人というのが増えているそうで、望みが薄いのも実情でしょう。

 このことは、レガシーデバイスは消える運命なので、特に問題があるとは思えません。PCが使えない奴は~とか言う老害は消えろと私は思います。(老害共は最低限アセンブラぐらい書けるんだよな?)
 スマホの方が学習コストが安く、PCとの効率がさして変わらないなら、スマホを使うというのは正しい選択でしょう。

 が、これが原因で現代人の眼がさらに過酷な環境に置かれることにならないことを、切に願うのみです。

近年神社に人が多いなぁと思うこの頃

あけましておめでとうございます。今年の抱負は再来年から本気出すです(・ω・)

元旦は皆さんはどのように過ごすんでしょうかね?

私は家族+犬とともに地元の神社に行ってあまりの参拝客の多さに何もせずに引き返してきました。別に、髪も仏も信じていませんし。死後の世界も知ったこっちゃなくて、信じているのは自らの脂肪のみです。あふん


さて、その地元の神社というのは、別に有名な神社とかじゃなくって、ほんとに地元に密着したというか、もう、本当に普通の神社。の、はずなんですが、なんと道にあふれるほどの行列。年々なんか人が増えている気がします。

単に私の地元の人口が増えてきているのか、それとも、SNS等の効果なのか、不景気で神頼みなのか、不毛で髪頼みなのか。


靖国神社の参拝客が年々増えているのと何らかの関係性はあるのでしょうかね?観測しているのが一つの神社しかないし、ちょっとググってみても資料が見当たらないので、ほかの神社や地域でどうなっているのか少し興味があります。




という感じで、今年からは、割と雑記的な内容や不確実だったり、すごく短い技術記事を増やしていこうかなと思います。


これまでこのブログは、それなりに調べてまとめた技術記事ばかりで、私事は書かない方針でやってきました。でも、大学生のころと違ってそんなに時間かけて書くがしんどくなって、更新回数がめちゃめちゃ減ってたんですよね。一つの記事が5000文字とか、1万文字とか多すぎかなと。私の妹の卒論が5万文字だったそうで、それでも多いほうだったそうですよ。なるべく、1000文字以内で収めたいですね。

あ、でも私クソニートなので時間だけは糞あるんですけどね。体重の問題ですね。間違えました、体力の問題ですね。

あとはツイッターがいつ潰れるのかわからん、ってのもありますね。ツイッターで済ましてたようなことも全部ブログのほうに持ってこようかなと。


後は今年の夏ぐらいにブログの内容をまとめたHPを作りたいなぁなんて考えています。これは体力いるからやるかどうかわからないけど。今のところあまりやる気なし。



というわけで、今年もよろしくお願いします。

なお、空白や改行、HTML含めてちょうどこの記事が1000文字です。だいぶ楽。

JavaFX9からPlatformに追加されるAPIについて

 この記事はJavaFX Advent Calender 2016の17日目の記事になります。
 前日はid:skrbさんのSooner or Later - JavaFX in the Boxでした。
 明日はid:aoe-tkさんです。


 前回今年最後とか言ったな。あれは嘘だ。

 はい、というわけで、なんとJavaFX Advent Calender 2016に二度も顔出しですよ。この引きこもりの王者クソニート様が。誰かお仕事くらさい

 というのもですね、JavaFX9で追加される新機能は前回紹介した内容以外にも、PulseListenerの追加やPlatform.startupやら、他にも小さな機能の追加があるのです。これらの使い道が私には思いつかなかったか、そもそもあまり興味を持っていなかったかなどの理由でスルーしたのですが、実はスルーしてた中でPlatformにNested Event Loop関連のAPIが追加になっていたようなのです!

 いや、もうマジでこれ知らなかった。知ってたら前回の記事に絶対追加してた。というわけで、興奮してついうっかり2回も顔出しすることにしました。ほんとスマンかった。

 なお、このことはJavaFX: New & Noteworthy(※PDF)にも書いてあります。しかし、Nested Event Loopの話がPulseとPulse Listenerのページの間に挟まれているんですよね。Pulseとかあまり興味ないせいか、読むのをすっ飛ばした様で気がつかなかったんですね~。アホですね~。

 というわけで、Platformに追加されるAPI(主にNested Event Loop)について解説します。



Platform.startup

 Application.launch使う限り使わないと思われるAPI。SWTとかSwingから使うときとかを想定している模様。

 JavaFX Application Threadを生成直後に引数に渡したRunnableを実行するとかそんな感じっぽいけど、普通に使う場面はないと思う。ないよね?思いつかなかったから前回もスルーしたんだけど。





Platform.requestNextPulse

 Scene.addPostLayoutPulseListener,Scene.addPreLayoutPulseListenerと共に追加されたPulse関連のAPI。次のPulseの要求が出来る。

 JavaFXには我々が扱うJavaFX Application Threadの他にPrism Render Threadという、画面描画用のスレッドがあるのですが、この描画スレッドとJavaFX Application Threadを同期するイベントがPulseになります。

 JavaFXってマウスイベントとかにNodeを追随させようとしても、妙にマウスの動きからNodeの動きがずれるのが、改善されたりしないかとか思ったけど、全くそんなことはなかったぜ。

 これらPulse関連APIの使い道は、今のところ私には思いつきません。PulseListenerの方はレイアウトにどれぐらい時間かかってるか、デバッグ的な使い方くらいかなぁ。私の頭じゃ。



 Pulseについての詳細は以下のページを参照してください。
docs.oracle.com


Nested Event Loop

 さて、今回の記事の本題です。といっても、実はこのNested Event LoopはJavaFX初期からあって、3年前の私の記事でも実はついでに紹介しています。
nodamushi.hatenablog.com


 大ざっぱに書くと、JavaFX Application Threadは通常以下のような処理をしています。

 Nested Event LoopのAPIを使うと、この流れを以下の図のように、複製することが可能になります。

 複製と言っても、新しいスレッドが走っているとかではなくて、単純に呼び出した関数の中でイベント処理のループが走り始めると言うだけです。


 この機能がおもにどこで使われているかというと、showAndWaitメソッドです。showAndWaitでウィンドウが閉じるまで処理を待機しても、JavaFX Application Threadは停止することなく、他の処理を続けることが出来ます。この時に使われているのがNested Event Loopです。


 今までは、Stage.showAndWaitからしかアクセスできなかったこのNested Event LoopのAPIが公開され、いつでもどこでも使えるようになります。
 一番簡単に思いつく使い道で、かつ、効果が高いのは、Stageのダイアログと同様にNode単位でのモーダルダイアログでしょう。

f:id:nodamushi:20161211183827p:plain


 今までは、このようなことを実現しようとしたら、次のような2つに分かれた手順が必要でした。

  1. なんらかのイベントハンドラ等からダイアログ用のNodeを作り、対象ノードの操作を不可能にする。(ここで最初のイベントハンドラの処理終了)
  2. ダイアログのYesやCancelといったボタンが押されたときに動作するイベントハンドラが、残りの処理を行い、ダイアログを消去する。

f:id:nodamushi:20161211183828p:plain

 しかし、Nested Event Loopを使うと、一つのイベントハンドラの中で処理が完結します。

f:id:nodamushi:20161211183829p:plain


 これはかなりプログラムを書くのが楽ですね。
 最近はJavaもラムダを導入したり、実質finalが導入されたりと色々工夫されて、イベントハンドラを書くのが楽になってきましたが、それでも処理が二分割されるようなプログラムを書くのって面倒くさいんですよね。
 処理が二分割で済めば良いですが、何回かダイアログを表示して、しかもそのダイアログ内容が途中で分岐するような処理だったら、面倒なことこの上ないですよね。

 一つのEvent Handlerで処理が済むというのは、それだけで実装面からも保守管理の点からもメリットです。




Nested Event Loopの使い方

Nested Event Loopを利用するには、以下のフィールド変数(ローカル変数は駄目)が必要です。

private Object waitKey = null;
  • waitKey: Nested Event Loopを解除するのに必要になるキー。なくしたらexitできないので要注意。final宣言してしまっても良い。

 フィールド変数を用意したら、後は以下の関数を定義しておけば良いでしょう。
 なお、基本的にはJavaFX Application Threadからしか操作しないので、waitKeyをvolatile等にする必要はありません。

private Object waitKey = null;

public boolean isWait(){return waitKey!=null;}

// 戻り値はintなどに決まっているなら、それに変換しても良い。
protected Object enterWait(){
  if(!isWait()){
    waitKey = new Object();
    Object result = Platform.enterNestedEventLoop(waitKey);
    return result;
  }
  return null;
}
// 引数のresultは上↑のresultになる。
protected void exitWait(Object result){
  if(isWait()){
    Object key=waitKey;
    waitKey=null;
    Platform.exitNestedEventLoop(key,result);
  }
}

 そしたら、後はこんな感じ。

//ダイアログのNodeを作成完了後
cancelButton.setOnAction(e->exitWait(0));
okButton.setOnAction(e->exitWait(1));
noButton.setOnAction(e->exitWait(2));

//何かにダイアログを追加
targetPane.getChildren().add( dialog );

//ダイアログのボタンが押されるまで待機
boolean isOK = enterWait() == 1;

//ダイアログを削除
targetPane.getChildren().remove( dialog );

if(isOK){//OKなら処理
…
}

 むろん、Dialogクラスのように、ダイアログ表示機能と待機機能を何かのクラスにまとめておいても良いでしょう。



 このように、Nested Event Loopを使えば、処理の途中でGUIを介した処理の結果が必要である場面の記述が簡単になります。色々夢が広がリングですね。


 というわけで、今度こそ、良いお年を。