ATOK Sparkのプラグインから駅すぱあとWeb APIを利用してみる話。

[ヴァル研究所 Advent Calendar 2016](http://qiita.com/advent-calendar/2016/val)、18日目は[ATOK Spark](https://github.com/JustSystems/ATOK_Spark)から駅すぱあとWeb APIを使ってみる例を紹介しようと思います。

## ATOK Sparkとは

[ATOK Spark](https://github.com/JustSystems/ATOK_Spark)は定型フレーズの展開機能を提供するソフトウェアです。例えばエディタ上で`gijiroku:<ENTER>`といった入力に対し、あらかじめ登録しておいたフレーズに展開することができます。
ソースコードを記述する際のスニペットや議事録のテンプレートを展開する[デモ](https://github.com/JustSystems/ATOK_Spark#-windowsでもmacでも使える)が紹介されています。また、Windows,Macの両方で利用できるという利点もあります。

以下のスライドでATOK Sparkの機能が紹介されています。

 * [ATOK Spark のご紹介とJavaによるプラグイン開発について](http://www.slideshare.net/JSUXDesign/atok-spark-java)

## ATOK Spark プラグインから駅すぱあとWeb APIを利用する

さて、この[ATOK Spark](https://github.com/JustSystems/ATOK_Spark)ですが、展開したいフレーズをXMLファイルで登録しておく他に、プラグインも作成できます。フレーズの展開をプラグインの形で行う場合は、動的に変化するパラメータを展開用フレーズに含めることが可能になります。

[5日目の記事](http://qiita.com/valsitoh/items/fa6d18adfcdc145394a1)ではRubyスクリプトから[駅すぱあと for web URL生成](http://qiita.com/valsitoh/items/fa6d18adfcdc145394a1)の機能を利用する例を紹介していました。今回はこの処理を[ATOK Spark](https://github.com/JustSystems/ATOK_Spark)のプラグインの形で再実装してみます。

 * [駅すぱあとWebサービス - フリープラン Documents](http://qiita.com/valsitoh/items/fa6d18adfcdc145394a1)

## プラグインのプロトコル

[先のスライド](http://www.slideshare.net/JSUXDesign/atok-spark-java)で解説されていますが、プラグインとATOK Sparkは標準入出力でやりとりするようです。プロトコルとしてやりとりできるコマンドはいくつか用意されていますが、最低限`CHECK`と`GETTEXT`を実装すればよさそうです。

プラグインのソースコードは以下のようになります。単に[5日目の記事](http://qiita.com/valsitoh/items/fa6d18adfcdc145394a1)で紹介したRubyスクリプトを`CHECK`と`GETTEXT`プロトコルに対応させただけです...。

```ruby
#!/usr/bin/env ruby
# coding: utf-8

require 'net/http'
require 'uri'
require 'erb'
include ERB::Util
require 'json'

# URLリクエストを投げる
def request(url_str)
  url = URI.parse(url_str)
  req = Net::HTTP::Get.new(url.request_uri)

  if ENV['http_proxy'] != nil
    ENV['http_proxy'] =~ /http:\/\/(.*?):(.*?)\//
    proxy = Net::HTTP::Proxy($1, $2)
    res = proxy.start(url.host, url.port) {|http|
      http.request(req)
    }
  else
    Net::HTTP.start(url.host, url.port) {|http|
      http.request(req)
    }
  end
end

# アクセスキー
key = ENV['EKISPERT_ACCESS_KEY']

# ATOK Sparkと接続するための識別文字列を返却します
puts "HELLO ATOK Spark/0.0"

# dataversionを取得する
url_str   = "http://api.ekispert.jp/v1/json/station/light?key=#{key}&name=#{url_encode(ARGV[0])}"
engine_version = JSON.parse(request(url_str).body)["ResultSet"]["engineVersion"]

result_url = ''

loop do
  line = gets.chomp
  next if line == nil

  if line =~ /CHECK \/search\/course\/light:(.*?):(.*?):/
    keyword = $1

    # 出発駅の駅コードを得る
    url_str = "http://api.ekispert.jp/v1/json/station/light?key=#{key}&name=#{url_encode($1)}"
    from_code = JSON.parse(request(url_str).body)["ResultSet"]["Point"][0]["Station"]["code"]

    # 到着駅の駅コードを得る
    url_str = "http://api.ekispert.jp/v1/json/station/light?key=#{key}&name=#{url_encode($2)}"
    to_code = JSON.parse(request(url_str).body)["ResultSet"]["Point"][0]["Station"]["code"]

    # 探索結果を駅すぱあと for webのURLとして取得する
    url_str = "http://api.ekispert.jp/v1/json/search/course/light?key=#{key}&from=#{from_code}&to=#{to_code}"
    result_url = JSON.parse(request(url_str).body)["ResultSet"]["ResourceURI"]

    puts "REPLACE 0" 
  elsif line =~ /GETTEXT 0$/
    puts "TEXT #{result_url}"
  end

  if line =~ /QUIT/
    break
  end
end
```

`CHECK /search/course/light:東京:高円寺:`のようなコマンドを入力すると、[駅すぱあと for web URL生成](http://qiita.com/valsitoh/items/fa6d18adfcdc145394a1)を呼び出し、探索結果のURLを取得します。`CHECK`コマンドの引数の形で入力された文字が渡されてくるようで、入力文字の一部をコマンド名と出発・到着駅として解釈することで、動的に変化するパラメータを処理することができます。レスポンスとして`REPLACE 0`を返していますが、これはプラグインの中でマッチしたフレーズのインデックスになります。今回は`CHECK /search/course/light:<出発駅>:<到着駅>:`の探索結果に対応するURLを1つだけ返す動作なので、インデックス`0`を返しています。

`GETTEXT`コマンドはATOK Sparkがプラグイン内でマッチしたフレーズを取得します。なので単に探索結果URLを`TEXT <探索結果URL>`の形で返します。

## 実際に試してみる

さっそく実際にプラグインの動作を試してみます。が、実際にエディタから`CHECK /search/course/light:東京:高円寺:`をうまく展開させられていないので、コマンドラインからの動作確認となります...。
(他のプラグインのドキュメントを見ると、`plugin.lst`に作成したプラグインの実行パスを記述すれば良いらしいのですが...)

実行例としては以下のようになります。標準入出力でプロトコルのコマンドを入力することで動作確認としています。

```
$ ./plugin/ekispert_webapi_sample/search_course_light.rb 
HELLO ATOK Spark/0.0
CHECK /search/course/light:東京:高円寺:
REPLACE 0
GETTEXT 0
TEXT http://roote.ekispert.net/result?arr=%E9%AB%98%E5%86%86%E5%AF%BA&arr_code=22671&connect=true&dep=%E6%9D%B1%E4%BA%AC&dep_code=22828&highway=true&hour&limitedExpress=true&liner=true&local=true&minute&plane=true&shinkansen=true&ship=true&sleep=false&sort=time&surcharge=3&type=dep&via1=&via1_code=&via2=&via2_code=
```

ちなみに、(当たり前といえばそうですが)ATOK Sparkにサンプルとして登録されているフレーズも`CHECK`コマンドで展開できます。
(なのでATOK Spark自体には接続できているようです)

```
CHECK main:
int main( int argc, char* argv[] )
{
        // パラメータ解析
        std::map<std::string,std::string> xMap;
        if( argc == 1 ){
                // 引数なし
        }else{
                // 引数あり
                xMap = parse( argc, argv);
        }

        ★ここにメインの処理を記載します
        return 0;
}
```

## まとめ

[ATOK Spark](https://github.com/JustSystems/ATOK_Spark)のプラグインから駅すぱあとWeb APIを呼び出してみました。今回は探索結果URLをフレーズとして展開しただけですが、経路探索結果をテキストで表示したり、時刻表情報を展開できるようにする等の応用例がありそうです。
(エディタからプラグインのフレーズ展開を呼び出す方法を調べ切れていないのが難ですが...)